Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Wed, 12 Dec 2018 07:12:07 +0200
changeset 450205 e27e7c02c708b052a53e39d35d81d8318d8730f1
parent 450165 5bcbe5232a262a2ad2703f2fd1678d8bd7681c02 (current diff)
parent 450204 6b08a22a9cedc9e6ec44cca96fbd49bc5c747878 (diff)
child 450214 41904e815f24b88b6191145e30e3cfda09ecc556
child 450234 140c4f3b6b137026f6305102d1e34725225352a9
push id35191
push userccoroiu@mozilla.com
push dateWed, 12 Dec 2018 05:12:41 +0000
treeherdermozilla-central@e27e7c02c708 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
e27e7c02c708 / 66.0a1 / 20181212051241 / files
nightly linux64
e27e7c02c708 / 66.0a1 / 20181212051241 / files
nightly mac
e27e7c02c708 / 66.0a1 / 20181212051241 / files
nightly win32
e27e7c02c708 / 66.0a1 / 20181212051241 / files
nightly win64
e27e7c02c708 / 66.0a1 / 20181212051241 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge
browser/extensions/formautofill/content/nameReferences.js
dom/ipc/TabParent.cpp
toolkit/components/telemetry/other/MemoryTelemetry.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5577,16 +5577,20 @@ nsBrowserAccess.prototype = {
 
   isTabContentWindow(aWindow) {
     return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
   },
 
   canClose() {
     return CanCloseWindow();
   },
+
+  get tabCount() {
+    return gBrowser.tabs.length;
+  },
 };
 
 function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
   var popup = aEvent.target;
   if (popup != aEvent.currentTarget)
     return;
 
   // Empty the menu
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -60,17 +60,16 @@ const whitelist = {
     "resource://gre/modules/Readerable.jsm",
     "resource://gre/modules/WebProgressChild.jsm",
 
     // Pocket
     "chrome://pocket/content/AboutPocket.jsm",
 
     // Telemetry
     "resource://gre/modules/TelemetryController.jsm", // bug 1470339
-    "resource://gre/modules/MemoryTelemetry.jsm", // bug 1481812
     "resource://gre/modules/TelemetryUtils.jsm", // bug 1470339
 
     // Extensions
     "resource://gre/modules/ExtensionUtils.jsm",
     "resource://gre/modules/MessageChannel.jsm",
   ]),
 };
 
--- a/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js
+++ b/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js
@@ -207,17 +207,17 @@ add_task(async function test_hiddenNonSh
     info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 
   Object.assign(expected, PTU.Addresses.TimBR, {
     "given-name": "Timothy-edit",
-    "name": "Timothy-edit João Berners-Lee",
+    "name": "Timothy-edit Jo\u{00E3}o Berners-Lee",
   });
   let actual = await formAutofillStorage.addresses.get(prefilledGuids.address1GUID);
   isnot(actual.email, "", "Check email isn't empty");
   // timeLastModified changes and isn't relevant
   delete actual.timeLastModified;
   delete expected.timeLastModified;
   SimpleTest.isDeeply(actual, expected, "Check profile didn't lose fields");
 
@@ -330,17 +330,17 @@ add_task(async function test_hiddenNonBi
     info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 
   Object.assign(expected, PTU.Addresses.TimBR, {
     "given-name": "Timothy-edit",
-    "name": "Timothy-edit João Berners-Lee",
+    "name": "Timothy-edit Jo\u{00E3}o Berners-Lee",
   });
   let actual = await formAutofillStorage.addresses.get(prefilledGuids.address1GUID);
   // timeLastModified changes and isn't relevant
   delete actual.timeLastModified;
   delete expected.timeLastModified;
   SimpleTest.isDeeply(actual, expected, "Check profile didn't lose fields");
 
   await cleanupFormAutofillStorage();
--- a/browser/extensions/formautofill/FormAutofillNameUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillNameUtils.jsm
@@ -1,32 +1,153 @@
 /* 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/. */
 
 "use strict";
 
-// Cu.import loads jsm files based on ISO-Latin-1 for now (see bug 530257).
-// However, the references about name parts include multi-byte characters.
-// Thus, we use |loadSubScript| to load the references instead.
-const NAME_REFERENCES = "chrome://formautofill/content/nameReferences.js";
-
 var EXPORTED_SYMBOLS = ["FormAutofillNameUtils"];
 
 ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
 
 // FormAutofillNameUtils is initially translated from
 // https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
 var FormAutofillNameUtils = {
-  // Will be loaded from NAME_REFERENCES.
-  NAME_PREFIXES: [],
-  NAME_SUFFIXES: [],
-  FAMILY_NAME_PREFIXES: [],
-  COMMON_CJK_MULTI_CHAR_SURNAMES: [],
-  KOREAN_MULTI_CHAR_SURNAMES: [],
+  NAME_PREFIXES: [
+    "1lt",
+    "1st",
+    "2lt",
+    "2nd",
+    "3rd",
+    "admiral",
+    "capt",
+    "captain",
+    "col",
+    "cpt",
+    "dr",
+    "gen",
+    "general",
+    "lcdr",
+    "lt",
+    "ltc",
+    "ltg",
+    "ltjg",
+    "maj",
+    "major",
+    "mg",
+    "mr",
+    "mrs",
+    "ms",
+    "pastor",
+    "prof",
+    "rep",
+    "reverend",
+    "rev",
+    "sen",
+    "st",
+  ],
+
+  NAME_SUFFIXES: [
+    "b.a",
+    "ba",
+    "d.d.s",
+    "dds",
+    "i",
+    "ii",
+    "iii",
+    "iv",
+    "ix",
+    "jr",
+    "m.a",
+    "m.d",
+    "ma",
+    "md",
+    "ms",
+    "ph.d",
+    "phd",
+    "sr",
+    "v",
+    "vi",
+    "vii",
+    "viii",
+    "x",
+  ],
+
+  FAMILY_NAME_PREFIXES: [
+    "d'",
+    "de",
+    "del",
+    "der",
+    "di",
+    "la",
+    "le",
+    "mc",
+    "san",
+    "st",
+    "ter",
+    "van",
+    "von",
+  ],
+
+  // The common and non-ambiguous CJK surnames (last names) that have more than
+  // one character.
+  COMMON_CJK_MULTI_CHAR_SURNAMES: [
+    // Korean, taken from the list of surnames:
+    // https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
+    "남궁",
+    "사공",
+    "서문",
+    "선우",
+    "제갈",
+    "황보",
+    "독고",
+    "망절",
+
+    // Chinese, taken from the top 10 Chinese 2-character surnames:
+    // https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
+    // Simplified Chinese (mostly mainland China)
+    "欧阳",
+    "令狐",
+    "皇甫",
+    "上官",
+    "司徒",
+    "诸葛",
+    "司马",
+    "宇文",
+    "呼延",
+    "端木",
+    // Traditional Chinese (mostly Taiwan)
+    "張簡",
+    "歐陽",
+    "諸葛",
+    "申屠",
+    "尉遲",
+    "司馬",
+    "軒轅",
+    "夏侯",
+  ],
+
+  // All Korean surnames that have more than one character, even the
+  // rare/ambiguous ones.
+  KOREAN_MULTI_CHAR_SURNAMES: [
+    "강전",
+    "남궁",
+    "독고",
+    "동방",
+    "망절",
+    "사공",
+    "서문",
+    "선우",
+    "소봉",
+    "어금",
+    "장곡",
+    "제갈",
+    "황목",
+    "황보",
+  ],
 
   // The whitespace definition based on
   // https://cs.chromium.org/chromium/src/base/strings/string_util_constants.cc?l=9&rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
   WHITESPACE: [
     "\u0009", // CHARACTER TABULATION
     "\u000A", // LINE FEED (LF)
     "\u000B", // LINE TABULATION
     "\u000C", // FORM FEED (FF)
@@ -199,18 +320,16 @@ var FormAutofillNameUtils = {
 
     return nameParts;
   },
 
   init() {
     if (this._dataLoaded) {
       return;
     }
-    let sandbox = FormAutofillUtils.loadDataFromScript(NAME_REFERENCES);
-    Object.assign(this, sandbox.nameReferences);
     this._dataLoaded = true;
 
     this.reCJK = new RegExp("[" + this.CJK_RANGE.join("") + "]", "u");
   },
 
   splitName(name) {
     let nameParts = {
       given: "",
deleted file mode 100644
--- a/browser/extensions/formautofill/content/nameReferences.js
+++ /dev/null
@@ -1,144 +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/. */
-
-/* exported nameReferences */
-
-"use strict";
-
-// The data below is initially copied from
-// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
-var nameReferences = {
-  NAME_PREFIXES: [
-    "1lt",
-    "1st",
-    "2lt",
-    "2nd",
-    "3rd",
-    "admiral",
-    "capt",
-    "captain",
-    "col",
-    "cpt",
-    "dr",
-    "gen",
-    "general",
-    "lcdr",
-    "lt",
-    "ltc",
-    "ltg",
-    "ltjg",
-    "maj",
-    "major",
-    "mg",
-    "mr",
-    "mrs",
-    "ms",
-    "pastor",
-    "prof",
-    "rep",
-    "reverend",
-    "rev",
-    "sen",
-    "st",
-  ],
-
-  NAME_SUFFIXES: [
-    "b.a",
-    "ba",
-    "d.d.s",
-    "dds",
-    "i",
-    "ii",
-    "iii",
-    "iv",
-    "ix",
-    "jr",
-    "m.a",
-    "m.d",
-    "ma",
-    "md",
-    "ms",
-    "ph.d",
-    "phd",
-    "sr",
-    "v",
-    "vi",
-    "vii",
-    "viii",
-    "x",
-  ],
-
-  FAMILY_NAME_PREFIXES: [
-    "d'",
-    "de",
-    "del",
-    "der",
-    "di",
-    "la",
-    "le",
-    "mc",
-    "san",
-    "st",
-    "ter",
-    "van",
-    "von",
-  ],
-
-  // The common and non-ambiguous CJK surnames (last names) that have more than
-  // one character.
-  COMMON_CJK_MULTI_CHAR_SURNAMES: [
-    // Korean, taken from the list of surnames:
-    // https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
-    "남궁",
-    "사공",
-    "서문",
-    "선우",
-    "제갈",
-    "황보",
-    "독고",
-    "망절",
-
-    // Chinese, taken from the top 10 Chinese 2-character surnames:
-    // https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
-    // Simplified Chinese (mostly mainland China)
-    "欧阳",
-    "令狐",
-    "皇甫",
-    "上官",
-    "司徒",
-    "诸葛",
-    "司马",
-    "宇文",
-    "呼延",
-    "端木",
-    // Traditional Chinese (mostly Taiwan)
-    "張簡",
-    "歐陽",
-    "諸葛",
-    "申屠",
-    "尉遲",
-    "司馬",
-    "軒轅",
-    "夏侯",
-  ],
-
-  // All Korean surnames that have more than one character, even the
-  // rare/ambiguous ones.
-  KOREAN_MULTI_CHAR_SURNAMES: [
-    "강전",
-    "남궁",
-    "독고",
-    "동방",
-    "망절",
-    "사공",
-    "서문",
-    "선우",
-    "소봉",
-    "어금",
-    "장곡",
-    "제갈",
-    "황목",
-    "황보",
-  ],
-};
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9798,38 +9798,27 @@ nsresult nsDocShell::DoURILoad(
   nsresult rv;
   nsCOMPtr<nsIURILoader> uriLoader =
       do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (IsFrame()) {
+    MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
+                   aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
+               "DoURILoad thinks this is a frame and InternalLoad does not");
+
+    // Only allow URLs able to return data in iframes.
     bool doesNotReturnData = false;
     NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
                         &doesNotReturnData);
-
     if (doesNotReturnData) {
-      // If this is an iframe, it must have a parent. Let's count the
-      // no-data-URL telemetry on the parent document, because probably this one
-      // is an about page.
-      nsCOMPtr<nsIDocShellTreeItem> parent;
-      GetSameTypeParent(getter_AddRefs(parent));
-      MOZ_ASSERT(parent);
-
-      nsIDocument* parentDocument = parent->GetDocument();
-      if (parentDocument) {
-        parentDocument->SetDocumentAndPageUseCounter(
-            eUseCounter_custom_no_data_URL);
-      }
-    }
-
-    MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
-                   aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
-               "DoURILoad thinks this is a frame and InternalLoad does not");
+      return NS_ERROR_UNKNOWN_PROTOCOL;
+    }
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
     // timing attacks to read data from cross-origin iframes. If this widens
     // we should add a protocol flag for whether the scheme is allowed in
     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
     nsCOMPtr<nsIURI> tempURI = aURI;
     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCounters.conf
@@ -55,19 +55,16 @@ method PushManager.subscribe
 method PushSubscription.unsubscribe
 
 // window.sidebar
 attribute Window.sidebar
 
 // External interface
 method External.AddSearchProvider
 
-// no-data URLs for iframes
-custom no_data_URL used in iframes
-
 // AppCache API
 method OfflineResourceList.swapCache
 method OfflineResourceList.update
 attribute OfflineResourceList.status
 attribute OfflineResourceList.onchecking
 attribute OfflineResourceList.onerror
 attribute OfflineResourceList.onnoupdate
 attribute OfflineResourceList.ondownloading
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -347,16 +347,18 @@ bool nsContentUtils::sFragmentParsingAct
 
 bool nsContentUtils::sDoNotTrackEnabled = false;
 
 bool nsContentUtils::sAntiTrackingControlCenterUIEnabled = false;
 
 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
 
 PopupControlState nsContentUtils::sPopupControlState = openAbused;
+uint32_t nsContentUtils::sPopupStatePusherCount = 0;
+bool nsContentUtils::sUnusedPopupToken = false;
 
 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
 
 // Subset of
 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
 enum AutocompleteUnsupportedFieldName : uint8_t {
 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
@@ -10360,17 +10362,18 @@ nsContentUtils::TryGetTabChildGlobal(nsI
 }
 
 /* static */ void nsContentUtils::InnerOrOuterWindowDestroyed() {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
   --sInnerOrOuterWindowCount;
 }
 
-/* static */ bool nsContentUtils::CanShowPopup(nsIPrincipal* aPrincipal) {
+/* static */ bool nsContentUtils::CanShowPopupByPermission(
+    nsIPrincipal* aPrincipal) {
   MOZ_ASSERT(aPrincipal);
   uint32_t permit;
   nsCOMPtr<nsIPermissionManager> permissionManager =
       services::GetPermissionManager();
 
   if (permissionManager &&
       NS_SUCCEEDED(permissionManager->TestPermissionFromPrincipal(
           aPrincipal, "popup", &permit))) {
@@ -10380,16 +10383,27 @@ nsContentUtils::TryGetTabChildGlobal(nsI
     if (permit == nsIPermissionManager::DENY_ACTION) {
       return false;
     }
   }
 
   return !sDisablePopups;
 }
 
+/* static */ bool nsContentUtils::TryUsePopupOpeningToken() {
+  MOZ_ASSERT(sPopupStatePusherCount);
+
+  if (!sUnusedPopupToken) {
+    sUnusedPopupToken = true;
+    return true;
+  }
+
+  return false;
+}
+
 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
   nsAString* result = static_cast<nsAString*>(aData);
   result->Append(static_cast<const char16_t*>(aBuf),
                  static_cast<uint32_t>(aLen));
   return true;
 }
 
 /* static */ bool nsContentUtils::StringifyJSON(
@@ -10482,8 +10496,20 @@ static bool JSONCreator(const char16_t* 
         host.Find(".", false, startIndexOfCurrentLevel + 1);
     if (startIndexOfNextLevel <= 0) {
       return false;
     }
     host = NS_LITERAL_CSTRING("*") +
            nsDependentCSubstring(host, startIndexOfNextLevel);
   }
 }
+
+/* static */ void nsContentUtils::PopupStatePusherCreated() {
+  ++sPopupStatePusherCount;
+}
+
+/* static */ void nsContentUtils::PopupStatePusherDestroyed() {
+  MOZ_ASSERT(sPopupStatePusherCount);
+
+  if (!--sPopupStatePusherCount) {
+    sUnusedPopupToken = false;
+  }
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3287,26 +3287,36 @@ class nsContentUtils {
 
   static void PopPopupControlState(PopupControlState aState) {
     MOZ_ASSERT(NS_IsMainThread());
     sPopupControlState = aState;
   }
 
   static PopupControlState GetPopupControlState() { return sPopupControlState; }
 
+  static void PopupStatePusherCreated();
+  static void PopupStatePusherDestroyed();
+
   // Get a serial number for a newly created inner or outer window.
   static uint32_t InnerOrOuterWindowCreated();
   // Record that an inner or outer window has been destroyed.
   static void InnerOrOuterWindowDestroyed();
   // Get the current number of inner or outer windows.
   static int32_t GetCurrentInnerOrOuterWindowCount() {
     return sInnerOrOuterWindowCount;
   }
 
-  static bool CanShowPopup(nsIPrincipal* aPrincipal);
+  // This method checks if the principal is allowed by open popups by user
+  // permissions. In this case, the caller should not block popups.
+  static bool CanShowPopupByPermission(nsIPrincipal* aPrincipal);
+
+  // This method returns true if the caller is allowed to show a popup, and it
+  // consumes the popup token for the current event. There is just 1 popup
+  // allowed per event.
+  static bool TryUsePopupOpeningToken();
 
   /**
    * Serializes a JSON-like JS::Value into a string.
    *
    * Usage:
    *   nsAutoString serializedValue;
    *   nsContentUtils::StringifyJSON(cx, &value, serializedValue);
    */
@@ -3490,16 +3500,21 @@ class nsContentUtils {
   // Alternate data mime type, used by the ScriptLoader to register and read the
   // bytecode out of the nsCacheInfoChannel.
   static nsCString* sJSBytecodeMimeType;
 
   static bool sDoNotTrackEnabled;
   static mozilla::LazyLogModule sDOMDumpLog;
 
   static PopupControlState sPopupControlState;
+  static uint32_t sPopupStatePusherCount;
+
+  // This token is by default set to false. When a popup/filePicker is shown, it
+  // is set to true.
+  static bool sUnusedPopupToken;
 
   static int32_t sInnerOrOuterWindowCount;
   static uint32_t sInnerOrOuterWindowSerialCounter;
 };
 
 /* static */ inline nsContentPolicyType
 nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType) {
   switch (aType) {
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5124,17 +5124,17 @@ bool nsGlobalWindowOuter::CanSetProperty
   }
 
   // If the pref is set to true, we can not set the property
   // and vice versa.
   return !Preferences::GetBool(aPrefName, true);
 }
 
 bool nsGlobalWindowOuter::PopupWhitelisted() {
-  if (mDoc && nsContentUtils::CanShowPopup(mDoc->NodePrincipal())) {
+  if (mDoc && nsContentUtils::CanShowPopupByPermission(mDoc->NodePrincipal())) {
     return true;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> parent = GetParent();
   if (parent == this) {
     return false;
   }
 
@@ -5178,18 +5178,19 @@ PopupControlState nsGlobalWindowOuter::R
     int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1);
     if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax)
       abuse = openOverridden;
   }
 
   // If this popup is allowed, let's block any other for this event, forcing
   // openBlocked state.
   if ((abuse == openAllowed || abuse == openControlled) &&
-      StaticPrefs::dom_block_multiple_popups() && !PopupWhitelisted()) {
-    nsContentUtils::PushPopupControlState(openBlocked, true);
+      StaticPrefs::dom_block_multiple_popups() && !PopupWhitelisted() &&
+      !nsContentUtils::TryUsePopupOpeningToken()) {
+    abuse = openBlocked;
   }
 
   return abuse;
 }
 
 /* If a window open is blocked, fire the appropriate DOM events. */
 void nsGlobalWindowOuter::FireAbuseEvents(
     const nsAString& aPopupURL, const nsAString& aPopupWindowName,
@@ -7385,18 +7386,21 @@ nsPIDOMWindowOuter::nsPIDOMWindowOuter(u
       mMarkedCCGeneration(0),
       mServiceWorkersTestingEnabled(false),
       mLargeAllocStatus(LargeAllocStatus::NONE) {}
 
 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() {}
 
 nsAutoPopupStatePusherInternal::nsAutoPopupStatePusherInternal(
     PopupControlState aState, bool aForce)
-    : mOldState(nsContentUtils::PushPopupControlState(aState, aForce)) {}
+    : mOldState(nsContentUtils::PushPopupControlState(aState, aForce)) {
+  nsContentUtils::PopupStatePusherCreated();
+}
 
 nsAutoPopupStatePusherInternal::~nsAutoPopupStatePusherInternal() {
   nsContentUtils::PopPopupControlState(mOldState);
+  nsContentUtils::PopupStatePusherDestroyed();
 }
 
 mozilla::dom::BrowsingContext* nsPIDOMWindowOuter::GetBrowsingContext() const {
   return mDocShell ? nsDocShell::Cast(mDocShell)->GetBrowsingContext()
                    : nullptr;
 }
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -647,20 +647,21 @@ bool HTMLInputElement::IsPopupBlocked() 
   nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
   MOZ_ASSERT(win, "window should not be null");
   if (!win) {
     return true;
   }
 
   // Check if page can open a popup without abuse regardless of allowed events
   if (win->GetPopupControlState() <= openBlocked) {
-    return false;
-  }
-
-  return !nsContentUtils::CanShowPopup(OwnerDoc()->NodePrincipal());
+    return !nsContentUtils::TryUsePopupOpeningToken();
+  }
+
+  return !nsContentUtils::CanShowPopupByPermission(
+             OwnerDoc()->NodePrincipal());
 }
 
 nsresult HTMLInputElement::InitColorPicker() {
   if (mPickerRunning) {
     NS_WARNING("Just one nsIColorPicker is allowed");
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/html/test/chrome.ini
+++ b/dom/html/test/chrome.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 support-files =
   file_anchor_ping.html
   image.png
 
 [test_anchor_ping.html]
 skip-if = os == 'android'
 [test_bug1414077.html]
+[test_multipleFilePicker.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_multipleFilePicker.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test for single filepicker per event</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <div id='foo'><a href='#'>Click here to test this issue</a></div>
+  <script>
+
+SimpleTest.waitForExplicitFinish();
+
+let clickCount = 0;
+let foo = document.getElementById('foo');
+foo.addEventListener('click', _ => {
+  if (++clickCount < 10) {
+    let input = document.createElement('input');
+    input.type = 'file';
+    foo.appendChild(input);
+    input.click();
+  }
+});
+
+let MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+let pickerCount = 0;
+
+// Let's do the first click.
+(new Promise(resolve => {
+  MockFilePicker.showCallback = function(filepicker) {
+    ++pickerCount;
+    resolve();
+  }
+  setTimeout(_ => {
+    is(pickerCount, 0, "No file picker initially");
+    sendMouseEvent({type:'click'}, 'foo');
+  }, 0);
+}))
+
+// Let's wait a bit more, then let's do a click.
+.then(_ => {
+  return new Promise(resolve => {
+    MockFilePicker.showCallback = function(filepicker) {
+      ++pickerCount;
+      resolve();
+    }
+
+    setTimeout(_ => {
+      is(pickerCount, 1, "Only 1 file picker");
+      is(clickCount, 10, "10 clicks triggered");
+      clickCount = 0;
+      pickerCount = 0;
+      synthesizeMouseAtCenter(foo, {});
+    }, 1000);
+  });
+})
+
+// Another click...
+.then(_ => {
+  setTimeout(_ => {
+    is(pickerCount, 1, "Only 1 file picker");
+    is(clickCount, 10, "10 clicks triggered");
+    MockFilePicker.cleanup();
+    SimpleTest.finish();
+  }, 1000);
+});
+
+</script>
+</body>
+</html>
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -168,10 +168,16 @@ interface nsIBrowserDOMWindow : nsISuppo
 
   /**
    * This function is responsible for calling
    * nsIContentViewer::PermitUnload on each frame in the window. It
    * returns true if closing the window is allowed. See canClose() in
    * BrowserUtils.jsm for a simple implementation of this method.
    */
   boolean canClose();
+
+  /**
+   * The number browser tabs in the window. This number currently includes
+   * lazy tabs, though for most uses it probably should not.
+   */
+  readonly attribute unsigned long tabCount;
 };
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -12,16 +12,17 @@
 
 #include "GeckoProfiler.h"
 #include "TabChild.h"
 #include "HandlerServiceChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/MemoryTelemetry.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/RemoteDecoderManagerChild.h"
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
@@ -236,16 +237,17 @@
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 #include "GMPServiceChild.h"
 #include "GfxInfoBase.h"
 #include "gfxPlatform.h"
 #include "nscore.h"  // for NS_FREE_PERMANENT_DATA
 #include "VRManagerChild.h"
 #include "private/pprio.h"
 #include "nsString.h"
+#include "MMPrinter.h"
 
 #ifdef MOZ_WIDGET_GTK
 #include "nsAppRunner.h"
 #endif
 
 #ifdef MOZ_CODE_COVERAGE
 #include "mozilla/CodeCoverageHandler.h"
 #endif
@@ -2253,16 +2255,17 @@ mozilla::ipc::IPCResult ContentChild::Re
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvAsyncMessage(
     const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentChild::RecvAsyncMessage",
                                              OTHER, aMsg);
+  MMPrinter::Print("ContentChild::RecvAsyncMessage", aMsg, aData);
 
   CrossProcessCpowHolder cpows(this, aCpows);
   RefPtr<nsFrameMessageManager> cpm =
       nsFrameMessageManager::GetChildProcessManager();
   if (cpm) {
     StructuredCloneData data;
     ipc::UnpackClonedMessageDataForChild(aData, data);
     cpm->ReceiveMessage(cpm, nullptr, aMsg, false, &data, &cpows, aPrincipal,
@@ -3371,16 +3374,22 @@ mozilla::ipc::IPCResult ContentChild::Re
   CodeCoverageHandler::FlushCounters();
   aResolver(/* unused */ true);
   return IPC_OK();
 #else
   MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
 #endif
 }
 
+mozilla::ipc::IPCResult ContentChild::RecvGetMemoryUniqueSetSize(
+    GetMemoryUniqueSetSizeResolver&& aResolver) {
+  MemoryTelemetry::Get().GetUniqueSetSize(std::move(aResolver));
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentChild::RecvSetInputEventQueueEnabled() {
   nsThreadManager::get().EnableMainThreadEventPrioritization();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvFlushInputEventQueue() {
   nsThreadManager::get().FlushInputEventPrioritization();
   return IPC_OK();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -607,16 +607,19 @@ class ContentChild final : public PConte
       nsTArray<IPC::Permission>&& aPerms) override;
 
   virtual mozilla::ipc::IPCResult RecvShareCodeCoverageMutex(
       const CrossProcessMutexHandle& aHandle) override;
 
   virtual mozilla::ipc::IPCResult RecvFlushCodeCoverageCounters(
       FlushCodeCoverageCountersResolver&& aResolver) override;
 
+  virtual mozilla::ipc::IPCResult RecvGetMemoryUniqueSetSize(
+      GetMemoryUniqueSetSizeResolver&& aResolver) override;
+
   virtual mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled() override;
 
   virtual mozilla::ipc::IPCResult RecvFlushInputEventQueue() override;
 
   virtual mozilla::ipc::IPCResult RecvSuspendInputEventQueue() override;
 
   virtual mozilla::ipc::IPCResult RecvResumeInputEventQueue() override;
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/MMPrinter.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MMPrinter.h"
+
+namespace mozilla {
+namespace dom {
+
+LazyLogModule MMPrinter::sMMLog("MessageManager");
+
+/* static */
+void MMPrinter::PrintImpl(char const* aLocation, const nsAString& aMsg,
+                          ClonedMessageData const& aData) {
+  NS_ConvertUTF16toUTF8 charMsg(aMsg);
+
+  /*
+   * The topic will be skipped if the topic name appears anywhere as a substring
+   * of the filter.
+   *
+   * Example:
+   *   MOZ_LOG_MESSAGEMANAGER_SKIP="foobar|extension"
+   *     Will  match the topics 'foobar', 'foo', 'bar', and 'ten' (even though
+   * you may not have intended to match the latter three) and it will not match
+   * the topics 'extensionresult' or 'Foo'.
+   */
+  char* mmSkipLog = PR_GetEnv("MOZ_LOG_MESSAGEMANAGER_SKIP");
+
+  if (mmSkipLog && strstr(mmSkipLog, charMsg.get())) {
+    return;
+  }
+
+  MOZ_LOG(MMPrinter::sMMLog, LogLevel::Debug,
+          ("%s Message: %s in process type: %s", aLocation, charMsg.get(),
+           XRE_ChildProcessTypeToString(XRE_GetProcessType())));
+
+  if (!MOZ_LOG_TEST(sMMLog, LogLevel::Verbose)) {
+    return;
+  }
+
+  ErrorResult rv;
+
+  AutoJSAPI jsapi;
+  MOZ_ALWAYS_TRUE(jsapi.Init(xpc::UnprivilegedJunkScope()));
+  JSContext* cx = jsapi.cx();
+
+  ipc::StructuredCloneData data;
+  ipc::UnpackClonedMessageDataForChild(aData, data);
+
+  /* Read original StructuredCloneData. */
+  JS::RootedValue scdContent(cx);
+  data.Read(cx, &scdContent, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    return;
+  }
+
+  JS::RootedString unevalObj(cx, JS_ValueToSource(cx, scdContent));
+  nsAutoJSString srcString;
+  if (!srcString.init(cx, unevalObj)) return;
+
+  MOZ_LOG(MMPrinter::sMMLog, LogLevel::Verbose,
+          ("   %s", NS_ConvertUTF16toUTF8(srcString).get()));
+  return;
+}
+
+}  // namespace dom
+}  // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/ipc/MMPrinter.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MMPrinter_h
+#define MMPrinter_h
+
+namespace mozilla {
+namespace dom {
+
+class MMPrinter {
+ public:
+  static void Print(char const* aLocation, const nsAString& aMsg,
+                    ClonedMessageData const& aData) {
+    if (MOZ_UNLIKELY(MOZ_LOG_TEST(MMPrinter::sMMLog, LogLevel::Debug))) {
+      MMPrinter::PrintImpl(aLocation, aMsg, aData);
+    }
+  }
+
+ private:
+  static LazyLogModule sMMLog;
+  static void PrintImpl(char const* aLocation, const nsAString& aMsg,
+                        ClonedMessageData const& aData);
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* MMPrinter_h */
\ No newline at end of file
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -647,16 +647,18 @@ child:
      * process ignores the update. Otherwise the content process updates its
      * list and reloads its plugins.
      **/
     async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins);
 
     async ShareCodeCoverageMutex(CrossProcessMutexHandle handle);
     async FlushCodeCoverageCounters() returns (bool unused);
 
+    async GetMemoryUniqueSetSize() returns (int64_t uss);
+
     /*
      * IPC message to enable the input event queue on the main thread of the
      * content process.
      */
     async SetInputEventQueueEnabled();
 
     /*
      * IPC message to flush the input event queue on the main thread of the
--- a/dom/ipc/SharedMap.cpp
+++ b/dom/ipc/SharedMap.cpp
@@ -9,16 +9,17 @@
 
 #include "MemMapSnapshot.h"
 #include "ScriptPreloader-inl.h"
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ScriptPreloader.h"
 
 using namespace mozilla::loader;
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -118,16 +118,17 @@
 #include "nsIHttpChannel.h"
 #include "mozilla/dom/DocGroup.h"
 #include "nsString.h"
 #include "nsISupportsPrimitives.h"
 #include "mozilla/Telemetry.h"
 #include "nsDocShellLoadState.h"
 #include "nsWebBrowser.h"
 #include "mozilla/dom/WindowGlobalChild.h"
+#include "MMPrinter.h"
 
 #ifdef XP_WIN
 #include "mozilla/plugins/PluginWidgetChild.h"
 #endif
 
 #ifdef NS_PRINTING
 #include "nsIPrintSession.h"
 #include "nsIPrintSettings.h"
@@ -2051,16 +2052,17 @@ mozilla::ipc::IPCResult TabChild::RecvLo
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabChild::RecvAsyncMessage(
     const nsString& aMessage, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabChild::RecvAsyncMessage",
                                              OTHER, aMessage);
+  MMPrinter::Print("TabChild::RecvAsyncMessage", aMessage, aData);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!mTabChildMessageManager) {
     return IPC_OK();
   }
 
   RefPtr<nsFrameMessageManager> mm =
       mTabChildMessageManager->GetMessageManager();
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1574,16 +1574,17 @@ bool TabParent::SendHandleTap(TapType aT
 }
 
 mozilla::ipc::IPCResult TabParent::RecvSyncMessage(
     const nsString& aMessage, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<StructuredCloneData>* aRetVal) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvSyncMessage",
                                              OTHER, aMessage);
+  MMPrinter::Print("TabParent::RecvSyncMessage", aMessage, aData);
 
   StructuredCloneData data;
   ipc::UnpackClonedMessageDataForParent(aData, data);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal)) {
     return IPC_FAIL_NO_REASON(this);
   }
@@ -1591,32 +1592,34 @@ mozilla::ipc::IPCResult TabParent::RecvS
 }
 
 mozilla::ipc::IPCResult TabParent::RecvRpcMessage(
     const nsString& aMessage, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<StructuredCloneData>* aRetVal) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvRpcMessage", OTHER,
                                              aMessage);
+  MMPrinter::Print("TabParent::RecvRpcMessage", aMessage, aData);
 
   StructuredCloneData data;
   ipc::UnpackClonedMessageDataForParent(aData, data);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::RecvAsyncMessage(
     const nsString& aMessage, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvAsyncMessage",
                                              OTHER, aMessage);
+  MMPrinter::Print("TabParent::RecvAsyncMessage", aMessage, aData);
 
   StructuredCloneData data;
   ipc::UnpackClonedMessageDataForParent(aData, data);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!ReceiveMessage(aMessage, false, &data, &cpows, aPrincipal, nullptr)) {
     return IPC_FAIL_NO_REASON(this);
   }
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -67,16 +67,17 @@ UNIFIED_SOURCES += [
     'ContentBridgeChild.cpp',
     'ContentBridgeParent.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'ContentProcessManager.cpp',
     'FilePickerParent.cpp',
     'MemMapSnapshot.cpp',
     'MemoryReportRequest.cpp',
+    'MMPrinter.cpp',
     'nsIContentChild.cpp',
     'nsIContentParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'SharedMap.cpp',
     'SharedStringMap.cpp',
     'StructuredCloneData.cpp',
@@ -143,16 +144,17 @@ LOCAL_INCLUDES += [
     '/dom/geolocation',
     '/dom/media/webspeech/synth/ipc',
     '/dom/security',
     '/extensions/cookie',
     '/extensions/spellcheck/src',
     '/gfx/2d',
     '/hal/sandbox',
     '/js/xpconnect/loader',
+    '/js/xpconnect/src',
     '/layout/base',
     '/media/webrtc',
     '/netwerk/base',
     '/toolkit/components/printingui/ipc',
     '/toolkit/crashreporter',
     '/toolkit/xre',
     '/uriloader/exthandler',
     '/widget',
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 
+#include "MMPrinter.h"
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
 namespace dom {
@@ -142,16 +143,17 @@ bool nsIContentChild::DeallocPFileDescri
   return true;
 }
 
 mozilla::ipc::IPCResult nsIContentChild::RecvAsyncMessage(
     const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
       "nsIContentChild::RecvAsyncMessage", OTHER, aMsg);
+  MMPrinter::Print("nsIContentChild::RecvAsyncMessage", aMsg, aData);
 
   CrossProcessCpowHolder cpows(this, aCpows);
   RefPtr<nsFrameMessageManager> cpm =
       nsFrameMessageManager::GetChildProcessManager();
   if (cpm) {
     ipc::StructuredCloneData data;
     ipc::UnpackClonedMessageDataForChild(aData, data);
 
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/Unused.h"
 
+#include "MMPrinter.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::jsipc;
 
 // XXX need another bug to move this to a common header.
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
@@ -230,16 +231,17 @@ bool nsIContentParent::DeallocPIPCBlobIn
 }
 
 mozilla::ipc::IPCResult nsIContentParent::RecvSyncMessage(
     const nsString& aMsg, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<ipc::StructuredCloneData>* aRetvals) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
       "nsIContentParent::RecvSyncMessage", OTHER, aMsg);
+  MMPrinter::Print("nsIContentParent::RecvSyncMessage", aMsg, aData);
 
   CrossProcessCpowHolder cpows(this, aCpows);
   RefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
     ipc::StructuredCloneData data;
     ipc::UnpackClonedMessageDataForParent(aData, data);
 
     ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, &cpows, aPrincipal,
@@ -249,16 +251,17 @@ mozilla::ipc::IPCResult nsIContentParent
 }
 
 mozilla::ipc::IPCResult nsIContentParent::RecvRpcMessage(
     const nsString& aMsg, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<ipc::StructuredCloneData>* aRetvals) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("nsIContentParent::RecvRpcMessage",
                                              OTHER, aMsg);
+  MMPrinter::Print("nsIContentParent::RecvRpcMessage", aMsg, aData);
 
   CrossProcessCpowHolder cpows(this, aCpows);
   RefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
     ipc::StructuredCloneData data;
     ipc::UnpackClonedMessageDataForParent(aData, data);
 
     ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, &cpows, aPrincipal,
@@ -300,16 +303,17 @@ bool nsIContentParent::DeallocPParentToC
   return true;
 }
 
 mozilla::ipc::IPCResult nsIContentParent::RecvAsyncMessage(
     const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
       "nsIContentParent::RecvAsyncMessage", OTHER, aMsg);
+  MMPrinter::Print("nsIContentParent::RecvAsyncMessage", aMsg, aData);
 
   CrossProcessCpowHolder cpows(this, aCpows);
   RefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
     ipc::StructuredCloneData data;
     ipc::UnpackClonedMessageDataForParent(aData, data);
 
     ppm->ReceiveMessage(ppm, nullptr, aMsg, false, &data, &cpows, aPrincipal,
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -1070,10 +1070,21 @@ void CriticalLogger::OutputMessage(const
 }
 
 void CriticalLogger::CrashAction(LogReason aReason) {
   if (Factory::GetLogForwarder()) {
     Factory::GetLogForwarder()->CrashAction(aReason);
   }
 }
 
+#ifdef WIN32
+void LogWStr(const wchar_t* aWStr, std::stringstream& aOut) {
+  int n = WideCharToMultiByte(CP_ACP, 0, aWStr, -1, nullptr, 0, nullptr, nullptr);
+  if (n > 1) {
+    std::vector<char> str(n);
+    WideCharToMultiByte(CP_ACP, 0, aWStr, -1, str.data(), n, nullptr, nullptr);
+    aOut << str.data();
+  }
+}
+#endif
+
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -247,16 +247,20 @@ struct Hexa {
   explicit Hexa(T aVal) : mVal(aVal) {}
   T mVal;
 };
 template <typename T>
 Hexa<T> hexa(T val) {
   return Hexa<T>(val);
 }
 
+#ifdef WIN32
+void LogWStr(const wchar_t* aStr, std::stringstream& aOut);
+#endif
+
 template <int L, typename Logger = BasicLogger>
 class Log {
  public:
   // The default is to have the prefix, have the new line, and for critical
   // logs assert on each call.
   static int DefaultOptions(bool aWithAssert = true) {
     return (int(LogOptions::AutoPrefix) |
             (aWithAssert ? int(LogOptions::AssertOnCall) : 0));
@@ -297,16 +301,24 @@ class Log {
     return *this;
   }
   Log& operator<<(const char aStr[]) {
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << static_cast<const char*>(aStr);
     }
     return *this;
   }
+#ifdef WIN32
+  Log& operator<<(const wchar_t aWStr[]) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      LogWStr(aWStr, mMessage);
+    }
+    return *this;
+  }
+#endif
   Log& operator<<(bool aBool) {
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << (aBool ? "true" : "false");
     }
     return *this;
   }
   Log& operator<<(int aInt) {
     if (MOZ_UNLIKELY(LogIt())) {
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -380,24 +380,74 @@ bool UnscaledFontDWrite::GetWRFontDescri
 
   UINT32 idx;
   BOOL exists;
   hr = systemFonts->FindFamilyName(familyName.data(), &idx, &exists);
   if (FAILED(hr) || !exists) {
     return false;
   }
 
+  // FIXME: Debugging kluge for bug 1455848. Remove once debugged!
+  UINT32 numFiles;
+  hr = mFontFace->GetFiles(&numFiles, nullptr);
+  if (FAILED(hr) || !numFiles) {
+    return false;
+  }
+  std::vector<RefPtr<IDWriteFontFile>> files;
+  files.resize(numFiles);
+  hr = mFontFace->GetFiles(&numFiles, getter_AddRefs(files[0]));
+  if (FAILED(hr)) {
+    return false;
+  }
+  MOZ_ASSERT(familyName.size() >= 1 && familyName.back() == 0);
+  for(auto& file : files) {
+    const void* key;
+    UINT32 keySize;
+    hr = file->GetReferenceKey(&key, &keySize);
+    if (FAILED(hr)) {
+      return false;
+    }
+    RefPtr<IDWriteFontFileLoader> loader;
+    hr = file->GetLoader(getter_AddRefs(loader));
+    if (FAILED(hr)) {
+      return false;
+    }
+    RefPtr<IDWriteLocalFontFileLoader> localLoader;
+    loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)getter_AddRefs(localLoader));
+    if (!localLoader) {
+      return false;
+    }
+    UINT32 pathLen;
+    hr = localLoader->GetFilePathLengthFromKey(key, keySize, &pathLen);
+    if (FAILED(hr)) {
+      return false;
+    }
+    size_t offset = familyName.size();
+    familyName.resize(offset + pathLen + 1);
+    hr = localLoader->GetFilePathFromKey(key, keySize, &familyName[offset], pathLen + 1);
+    if (FAILED(hr)) {
+      return false;
+    }
+    MOZ_ASSERT(familyName.back() == 0);
+    DWORD attribs = GetFileAttributesW(&familyName[offset]);
+    if (attribs == INVALID_FILE_ATTRIBUTES) {
+      gfxCriticalNote << "sending font family \"" << &familyName[0]
+                      << "\" with invalid file \"" << &familyName[offset]
+                      << "\"";
+    }
+  }
+
   // The style information that identifies the font can be encoded easily in
   // less than 32 bits. Since the index is needed for font descriptors, only
   // the family name and style information, pass along the style in the index
   // data to avoid requiring a more complicated structure packing for it in
   // the data payload.
   uint32_t index = weight | (stretch << 16) | (style << 24);
   aCb(reinterpret_cast<const uint8_t*>(familyName.data()),
-      (familyName.size() - 1) * sizeof(WCHAR), index, aBaton);
+      familyName.size() * sizeof(WCHAR), index, aBaton);
   return true;
 }
 
 ScaledFontDWrite::InstanceData::InstanceData(
     const wr::FontInstanceOptions* aOptions,
     const wr::FontInstancePlatformOptions* aPlatformOptions)
     : mUseEmbeddedBitmap(false),
       mForceGDIMode(false),
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -77,16 +77,19 @@ parent:
   sync SetTestSampleTime(TimeStamp sampleTime);
   sync LeaveTestMode();
   sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value);
   sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
   sync SetAsyncZoom(ViewID scrollId, float zoom);
   async FlushApzRepaints();
   sync GetAPZTestData() returns (APZTestData data);
 
+  // Debugging routine for bug 1455848.
+  async ValidateFontDescriptor(uint8_t[] desc);
+
   async Shutdown();
   sync ShutdownSync();
 child:
   async WrUpdated(IdNamespace aNewIdNamespace, TextureFactoryIdentifier textureFactoryIdentifier);
   async WrReleasedImages(ExternalImageKeyPair[] pairs);
   async __delete__();
 };
 
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -220,16 +220,24 @@ static void WriteFontFileData(const uint
 }
 
 static void WriteFontDescriptor(const uint8_t* aData, uint32_t aLength,
                                 uint32_t aIndex, void* aBaton) {
   FontFileDataSink* sink = static_cast<FontFileDataSink*>(aBaton);
 
   *sink->mFontKey = sink->mWrBridge->GetNextFontKey();
 
+#ifdef XP_WIN
+  // FIXME: Debugging kluge for bug 1455848. Remove once debugged!
+  nsTArray<uint8_t> data;
+  data.AppendElements(aData, aLength);
+  sink->mWrBridge->SendValidateFontDescriptor(data);
+  aLength = uint32_t(wcsnlen_s((const wchar_t*)aData, aLength / sizeof(wchar_t)) * sizeof(wchar_t));
+#endif
+
   sink->mResources->AddFontDescriptor(
       *sink->mFontKey, Range<uint8_t>(const_cast<uint8_t*>(aData), aLength),
       aIndex);
 }
 
 void WebRenderBridgeChild::PushGlyphs(
     wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
     gfx::ScaledFont* aFont, const wr::ColorF& aColor,
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -30,16 +30,20 @@
 #include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
+#ifdef XP_WIN
+#include "dwrite.h"
+#endif
+
 using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 
 bool is_in_main_thread() { return NS_IsMainThread(); }
 
@@ -692,16 +696,58 @@ void WebRenderBridgeParent::ObserveShare
   if (!mDestroyed) {
     Unused << SendWrReleasedImages(aPairs);
   }
   for (const auto& pair : aPairs) {
     SharedSurfacesParent::Release(pair.id);
   }
 }
 
+// Debugging kluge for bug 1455848. Remove once debugged!
+mozilla::ipc::IPCResult WebRenderBridgeParent::RecvValidateFontDescriptor(
+    nsTArray<uint8_t>&& aData) {
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+#ifdef XP_WIN
+  nsTArray<uint8_t> data(aData);
+  wchar_t* family = (wchar_t*)data.Elements();
+  size_t remaining = data.Length() / sizeof(wchar_t);
+  size_t familyLength = wcsnlen_s(family, remaining);
+  MOZ_ASSERT(familyLength < remaining && family[familyLength] == 0);
+  remaining -= familyLength + 1;
+  wchar_t* files = family + familyLength + 1;
+  BOOL exists = FALSE;
+  if (RefPtr<IDWriteFontCollection> systemFonts = Factory::GetDWriteSystemFonts()) {
+    UINT32 idx;
+    systemFonts->FindFamilyName(family, &idx, &exists);
+  }
+  if (!remaining) {
+    gfxCriticalNote << (exists ? "found" : "MISSING")
+                    << " font family \"" << family
+                    << "\" has no files!";
+  }
+  while (remaining > 0) {
+    size_t fileLength = wcsnlen_s(files, remaining);
+    MOZ_ASSERT(fileLength < remaining && files[fileLength] == 0);
+    DWORD attribs = GetFileAttributesW(files);
+    if (!exists || attribs == INVALID_FILE_ATTRIBUTES) {
+      gfxCriticalNote << (exists ? "found" : "MISSING")
+                      << " font family \"" << family
+                      << "\" has " << (attribs == INVALID_FILE_ATTRIBUTES ? "INVALID" : "valid")
+                      << " file \"" << files
+                      << "\"";
+    }
+    remaining -= fileLength + 1;
+    files += fileLength + 1;
+  }
+#endif
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateResources(
     nsTArray<OpUpdateResource>&& aResourceUpdates,
     nsTArray<RefCountedShmem>&& aSmallShmems,
     nsTArray<ipc::Shmem>&& aLargeShmems) {
   if (mDestroyed) {
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
     return IPC_OK();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -118,16 +118,19 @@ class WebRenderBridgeParent final : publ
       const TimeStamp& aTxnStartTime, const nsCString& aTxnURL,
       const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvSetFocusTarget(
       const FocusTarget& aFocusTarget) override;
   mozilla::ipc::IPCResult RecvParentCommands(
       nsTArray<WebRenderParentCommand>&& commands) override;
   mozilla::ipc::IPCResult RecvGetSnapshot(PTextureParent* aTexture) override;
 
+  mozilla::ipc::IPCResult RecvValidateFontDescriptor(
+      nsTArray<uint8_t>&& aData) override;
+
   mozilla::ipc::IPCResult RecvSetLayersObserverEpoch(
       const LayersObserverEpoch& aChildEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvScheduleComposite() override;
   mozilla::ipc::IPCResult RecvCapture() override;
   mozilla::ipc::IPCResult RecvSyncWithCompositor() override;
 
--- a/js/public/CompilationAndEvaluation.h
+++ b/js/public/CompilationAndEvaluation.h
@@ -273,16 +273,25 @@ extern JS_PUBLIC_API bool CompileForNonS
  * user currently needs it.  Such way could be added in the future if it's ever
  * needed.
  */
 extern JS_PUBLIC_API bool CompileLatin1ForNonSyntacticScope(
     JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes,
     size_t length, MutableHandle<JSScript*> script);
 
 /**
+ * Compile the given UTF-8 data for non-syntactic scope.
+ *
+ * An exception is thrown if the data isn't valid UTF-8.
+ */
+extern JS_PUBLIC_API bool CompileUtf8ForNonSyntacticScope(
+    JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes,
+    size_t length, MutableHandle<JSScript*> script);
+
+/**
  * Compile a function with envChain plus the global as its scope chain.
  * envChain must contain objects in the current compartment of cx.  The actual
  * scope chain used for the function will consist of With wrappers for those
  * objects, followed by the current global of the compartment cx is in.  This
  * global must not be explicitly included in the scope chain.
  */
 extern JS_PUBLIC_API bool CompileFunction(JSContext* cx,
                                           AutoVector<JSObject*>& envChain,
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -769,20 +769,20 @@ bool NodeBuilder::newNodeLoc(TokenPos* p
   if (!newObject(&loc)) {
     return false;
   }
 
   dst.setObject(*loc);
 
   uint32_t startLineNum, startColumnIndex;
   uint32_t endLineNum, endColumnIndex;
-  parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum,
-                                                   &startColumnIndex);
-  parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum,
-                                                   &endColumnIndex);
+  parser->tokenStream.computeLineAndColumn(pos->begin, &startLineNum,
+                                           &startColumnIndex);
+  parser->tokenStream.computeLineAndColumn(pos->end, &endLineNum,
+                                           &endColumnIndex);
 
   if (!newObject(&to)) {
     return false;
   }
   val.setObject(*to);
   if (!defineProperty(loc, "start", val)) {
     return false;
   }
@@ -1864,18 +1864,19 @@ bool ASTSerializer::blockStatement(ListN
   NodeVector stmts(cx);
   return statements(node, stmts) &&
          builder.blockStatement(stmts, &node->pn_pos, dst);
 }
 
 bool ASTSerializer::program(ListNode* node, MutableHandleValue dst) {
 #ifdef DEBUG
   {
-    const auto& srcCoords = parser->anyChars.srcCoords;
-    MOZ_ASSERT(srcCoords.lineNum(node->pn_pos.begin) == lineno);
+    const TokenStreamAnyChars& anyChars = parser->anyChars;
+    auto lineToken = anyChars.lineToken(node->pn_pos.begin);
+    MOZ_ASSERT(anyChars.lineNumber(lineToken) == lineno);
   }
 #endif
 
   NodeVector stmts(cx);
   return statements(node, stmts) && builder.program(stmts, &node->pn_pos, dst);
 }
 
 bool ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst) {
--- a/js/src/frontend/EitherParser.h
+++ b/js/src/frontend/EitherParser.h
@@ -80,32 +80,32 @@ struct ParserOptions {
 
 template <class Parser>
 struct ParserNewObjectBox {
   static constexpr auto get() -> decltype(&Parser::newObjectBox) {
     return &Parser::newObjectBox;
   }
 };
 
+template <class TokenStream>
+struct TokenStreamComputeLineAndColumn {
+  static constexpr auto get() -> decltype(&TokenStream::computeLineAndColumn) {
+    return &TokenStream::computeLineAndColumn;
+  }
+};
+
 // Generic matchers.
 
 struct ParseHandlerMatcher {
   template <class Parser>
   frontend::FullParseHandler& match(Parser* parser) {
     return parser->handler;
   }
 };
 
-struct AnyCharsMatcher {
-  template <class Parser>
-  frontend::TokenStreamAnyChars& match(Parser* parser) {
-    return parser->anyChars;
-  }
-};
-
 struct ParserBaseMatcher {
   template <class Parser>
   frontend::ParserBase& match(Parser* parser) {
     return *static_cast<frontend::ParserBase*>(parser);
   }
 };
 
 struct ErrorReporterMatcher {
@@ -157,22 +157,22 @@ class EitherParser : public BCEParserHan
 
   ObjectBox* newObjectBox(JSObject* obj) final {
     InvokeMemberFunction<detail::GetParser, detail::ParserNewObjectBox,
                          JSObject*>
         matcher{obj};
     return parser.match(std::move(matcher));
   }
 
-  const TokenStreamAnyChars& anyChars() const {
-    return parser.match(detail::AnyCharsMatcher());
-  }
-
   void computeLineAndColumn(uint32_t offset, uint32_t* line,
                             uint32_t* column) const {
-    return anyChars().lineAndColumnAt(offset, line, column);
+    InvokeMemberFunction<detail::GetTokenStream,
+                         detail::TokenStreamComputeLineAndColumn, uint32_t,
+                         uint32_t*, uint32_t*>
+        matcher{offset, line, column};
+    return parser.match(std::move(matcher));
   }
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_EitherParser_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -612,17 +612,17 @@ void GeneralParser<ParseHandler, Unit>::
     unsigned errorNumber, unsigned noteNumber, uint32_t openedPos) {
   auto notes = MakeUnique<JSErrorNotes>();
   if (!notes) {
     ReportOutOfMemory(pc->sc()->context);
     return;
   }
 
   uint32_t line, column;
-  anyChars.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
+  tokenStream.computeLineAndColumn(openedPos, &line, &column);
 
   const size_t MaxWidth = sizeof("4294967295");
   char columnNumber[MaxWidth];
   SprintfLiteral(columnNumber, "%" PRIu32, column);
   char lineNumber[MaxWidth];
   SprintfLiteral(lineNumber, "%" PRIu32, line);
 
   if (!notes->addNoteASCII(pc->sc()->context, getFilename(), line, column,
@@ -651,17 +651,17 @@ void GeneralParser<ParseHandler, Unit>::
 
   auto notes = MakeUnique<JSErrorNotes>();
   if (!notes) {
     ReportOutOfMemory(pc->sc()->context);
     return;
   }
 
   uint32_t line, column;
-  anyChars.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
+  tokenStream.computeLineAndColumn(prevPos, &line, &column);
 
   const size_t MaxWidth = sizeof("4294967295");
   char columnNumber[MaxWidth];
   SprintfLiteral(columnNumber, "%" PRIu32, column);
   char lineNumber[MaxWidth];
   SprintfLiteral(lineNumber, "%" PRIu32, line);
 
   if (!notes->addNoteASCII(pc->sc()->context, getFilename(), line, column,
@@ -2432,17 +2432,17 @@ bool GeneralParser<ParseHandler, Unit>::
                                               : JSMSG_PAREN_BEFORE_FORMAL);
       return false;
     }
 
     firstTokenPos = pos();
 
     // Record the start of function source (for FunctionToString). If we
     // are parenFreeArrow, we will set this below, after consuming the NAME.
-    funbox->setStart(anyChars);
+    tokenStream.setFunctionStart(funbox);
   } else {
     // When delazifying, we may not have a current token and pos() is
     // garbage. In that case, substitute the first token's position.
     if (!tokenStream.peekTokenPos(&firstTokenPos, firstTokenModifier)) {
       return false;
     }
   }
 
@@ -2547,17 +2547,17 @@ bool GeneralParser<ParseHandler, Unit>::
 
         default: {
           if (!TokenKindIsPossibleIdentifier(tt)) {
             error(JSMSG_MISSING_FORMAL);
             return false;
           }
 
           if (parenFreeArrow) {
-            funbox->setStart(anyChars);
+            tokenStream.setFunctionStart(funbox);
           }
 
           RootedPropertyName name(context, bindingIdentifier(yieldHandling));
           if (!name) {
             return false;
           }
 
           if (!notePositionalFormalParameter(funNode, name, pos().begin,
@@ -3297,26 +3297,31 @@ bool GeneralParser<ParseHandler, Unit>::
     if (!tokenStream.getToken(&actual, TokenStream::Operand)) {
       return false;
     }
     if (actual != TokenKind::RightCurly) {
       reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED,
                            openedPos);
       return false;
     }
+
     funbox->setEnd(anyChars);
   } else {
     MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow);
 
     if (anyChars.hadError()) {
       return false;
     }
+
     funbox->setEnd(anyChars);
-    if (kind == FunctionSyntaxKind::Statement && !matchOrInsertSemicolon()) {
-      return false;
+
+    if (kind == FunctionSyntaxKind::Statement) {
+      if (!matchOrInsertSemicolon()) {
+        return false;
+      }
     }
   }
 
   if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject()) {
     funbox->setNeedsHomeObject();
   }
 
   if (!finishFunction(isStandaloneFunction)) {
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -522,38 +522,47 @@ class FunctionBox : public ObjectBox, pu
 
   // Return whether this or an enclosing function is being parsed and
   // validated as asm.js. Note: if asm.js validation fails, this will be false
   // while the function is being reparsed. This flag can be used to disable
   // certain parsing features that are necessary in general, but unnecessary
   // for validated asm.js.
   bool useAsmOrInsideUseAsm() const { return useAsm; }
 
-  void setStart(const TokenStreamAnyChars& anyChars) {
-    uint32_t offset = anyChars.currentToken().pos.begin;
-    setStart(anyChars, offset);
-  }
-
-  void setStart(const TokenStreamAnyChars& anyChars, uint32_t offset) {
+  void setStart(uint32_t offset, uint32_t line, uint32_t column) {
     bufStart = offset;
-    anyChars.srcCoords.lineNumAndColumnIndex(offset, &startLine, &startColumn);
+    startLine = line;
+    startColumn = column;
   }
 
   void setEnd(const TokenStreamAnyChars& anyChars) {
     // For all functions except class constructors, the buffer and
     // toString ending positions are the same. Class constructors override
     // the toString ending position with the end of the class definition.
     uint32_t offset = anyChars.currentToken().pos.end;
     bufEnd = offset;
     toStringEnd = offset;
   }
 
   void trace(JSTracer* trc) override;
 };
 
+template <typename Unit, class AnyCharsAccess>
+inline void GeneralTokenStreamChars<Unit, AnyCharsAccess>::setFunctionStart(
+    FunctionBox* funbox) const {
+  const TokenStreamAnyChars& anyChars = anyCharsAccess();
+
+  uint32_t bufStart = anyChars.currentToken().pos.begin;
+
+  uint32_t startLine, startColumn;
+  computeLineAndColumn(bufStart, &startLine, &startColumn);
+
+  funbox->setStart(bufStart, startLine, startColumn);
+}
+
 inline FunctionBox* SharedContext::asFunctionBox() {
   MOZ_ASSERT(isFunctionBox());
   return static_cast<FunctionBox*>(this);
 }
 
 // In generators, we treat all bindings as closed so that they get stored on
 // the heap.  This way there is less information to copy off the stack when
 // suspending, and back on when resuming.  It also avoids the need to create
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -322,69 +322,70 @@ PropertyName* TokenStreamAnyChars::reser
     FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
 #undef EMIT_CASE
     default:
       MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
   }
   return nullptr;
 }
 
-TokenStreamAnyChars::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln,
-                                                uint32_t col,
-                                                uint32_t initialLineOffset)
+TokenStreamAnyChars::SourceCoords::SourceCoords(JSContext* cx,
+                                                uint32_t initialLineNumber,
+                                                uint32_t initialColumnNumber,
+                                                uint32_t initialOffset)
     : lineStartOffsets_(cx),
-      initialLineNum_(ln),
-      initialColumn_(col),
-      lastLineIndex_(0) {
+      initialLineNum_(initialLineNumber),
+      initialColumn_(initialColumnNumber),
+      lastIndex_(0) {
   // This is actually necessary!  Removing it causes compile errors on
   // GCC and clang.  You could try declaring this:
   //
   //   const uint32_t TokenStreamAnyChars::SourceCoords::MAX_PTR;
   //
   // which fixes the GCC/clang error, but causes bustage on Windows.  Sigh.
   //
   uint32_t maxPtr = MAX_PTR;
 
-  // The first line begins at buffer offset |initialLineOffset|.  MAX_PTR is
-  // the sentinel.  The appends cannot fail because |lineStartOffsets_| has
+  // The first line begins at buffer offset |initialOffset|.  MAX_PTR is the
+  // sentinel.  The appends cannot fail because |lineStartOffsets_| has
   // statically-allocated elements.
   MOZ_ASSERT(lineStartOffsets_.capacity() >= 2);
   MOZ_ALWAYS_TRUE(lineStartOffsets_.reserve(2));
-  lineStartOffsets_.infallibleAppend(initialLineOffset);
+  lineStartOffsets_.infallibleAppend(initialOffset);
   lineStartOffsets_.infallibleAppend(maxPtr);
 }
 
 MOZ_ALWAYS_INLINE bool TokenStreamAnyChars::SourceCoords::add(
     uint32_t lineNum, uint32_t lineStartOffset) {
-  uint32_t lineIndex = lineNumToIndex(lineNum);
+  uint32_t index = indexFromLineNumber(lineNum);
   uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
 
-  MOZ_ASSERT(lineStartOffsets_[0] <= lineStartOffset &&
-             lineStartOffsets_[sentinelIndex] == MAX_PTR);
-
-  if (lineIndex == sentinelIndex) {
+  MOZ_ASSERT(lineStartOffsets_[0] <= lineStartOffset);
+  MOZ_ASSERT(lineStartOffsets_[sentinelIndex] == MAX_PTR);
+
+  if (index == sentinelIndex) {
     // We haven't seen this newline before.  Update lineStartOffsets_
     // only if lineStartOffsets_.append succeeds, to keep sentinel.
     // Otherwise return false to tell TokenStream about OOM.
     uint32_t maxPtr = MAX_PTR;
     if (!lineStartOffsets_.append(maxPtr)) {
       static_assert(mozilla::IsSame<decltype(lineStartOffsets_.allocPolicy()),
                                     TempAllocPolicy&>::value,
                     "this function's caller depends on it reporting an "
                     "error on failure, as TempAllocPolicy ensures");
       return false;
     }
 
-    lineStartOffsets_[lineIndex] = lineStartOffset;
+    lineStartOffsets_[index] = lineStartOffset;
   } else {
     // We have seen this newline before (and ungot it).  Do nothing (other
     // than checking it hasn't mysteriously changed).
-    // This path can be executed after hitting OOM, so check lineIndex.
-    MOZ_ASSERT_IF(lineIndex < sentinelIndex,
-                  lineStartOffsets_[lineIndex] == lineStartOffset);
+    // This path can be executed after hitting OOM, so check index.
+    MOZ_ASSERT_IF(index < sentinelIndex,
+                  lineStartOffsets_[index] == lineStartOffset);
   }
   return true;
 }
 
 MOZ_ALWAYS_INLINE bool TokenStreamAnyChars::SourceCoords::fill(
     const TokenStreamAnyChars::SourceCoords& other) {
   MOZ_ASSERT(lineStartOffsets_[0] == other.lineStartOffsets_[0]);
   MOZ_ASSERT(lineStartOffsets_.back() == MAX_PTR);
@@ -402,43 +403,43 @@ MOZ_ALWAYS_INLINE bool TokenStreamAnyCha
     if (!lineStartOffsets_.append(other.lineStartOffsets_[i])) {
       return false;
     }
   }
   return true;
 }
 
 MOZ_ALWAYS_INLINE uint32_t
-TokenStreamAnyChars::SourceCoords::lineIndexOf(uint32_t offset) const {
+TokenStreamAnyChars::SourceCoords::indexFromOffset(uint32_t offset) const {
   uint32_t iMin, iMax, iMid;
 
-  if (lineStartOffsets_[lastLineIndex_] <= offset) {
+  if (lineStartOffsets_[lastIndex_] <= offset) {
     // If we reach here, offset is on a line the same as or higher than
     // last time.  Check first for the +0, +1, +2 cases, because they
     // typically cover 85--98% of cases.
-    if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
-      return lastLineIndex_;  // lineIndex is same as last time
+    if (offset < lineStartOffsets_[lastIndex_ + 1]) {
+      return lastIndex_;  // index is same as last time
     }
 
     // If we reach here, there must be at least one more entry (plus the
     // sentinel).  Try it.
-    lastLineIndex_++;
-    if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
-      return lastLineIndex_;  // lineIndex is one higher than last time
+    lastIndex_++;
+    if (offset < lineStartOffsets_[lastIndex_ + 1]) {
+      return lastIndex_;  // index is one higher than last time
     }
 
     // The same logic applies here.
-    lastLineIndex_++;
-    if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
-      return lastLineIndex_;  // lineIndex is two higher than last time
+    lastIndex_++;
+    if (offset < lineStartOffsets_[lastIndex_ + 1]) {
+      return lastIndex_;  // index is two higher than last time
     }
 
     // No luck.  Oh well, we have a better-than-default starting point for
     // the binary search.
-    iMin = lastLineIndex_ + 1;
+    iMin = lastIndex_ + 1;
     MOZ_ASSERT(iMin <
                lineStartOffsets_.length() - 1);  // -1 due to the sentinel
 
   } else {
     iMin = 0;
   }
 
   // This is a binary search with deferred detection of equality, which was
@@ -449,37 +450,28 @@ TokenStreamAnyChars::SourceCoords::lineI
   while (iMax > iMin) {
     iMid = iMin + (iMax - iMin) / 2;
     if (offset >= lineStartOffsets_[iMid + 1]) {
       iMin = iMid + 1;  // offset is above lineStartOffsets_[iMid]
     } else {
       iMax = iMid;  // offset is below or within lineStartOffsets_[iMid]
     }
   }
+
   MOZ_ASSERT(iMax == iMin);
-  MOZ_ASSERT(lineStartOffsets_[iMin] <= offset &&
-             offset < lineStartOffsets_[iMin + 1]);
-  lastLineIndex_ = iMin;
+  MOZ_ASSERT(lineStartOffsets_[iMin] <= offset);
+  MOZ_ASSERT(offset < lineStartOffsets_[iMin + 1]);
+
+  lastIndex_ = iMin;
   return iMin;
 }
 
-uint32_t TokenStreamAnyChars::SourceCoords::lineNum(uint32_t offset) const {
-  uint32_t lineIndex = lineIndexOf(offset);
-  return lineIndexToNum(lineIndex);
-}
-
-uint32_t TokenStreamAnyChars::SourceCoords::columnIndex(uint32_t offset) const {
-  return lineIndexAndOffsetToColumn(lineIndexOf(offset), offset);
-}
-
-void TokenStreamAnyChars::SourceCoords::lineNumAndColumnIndex(
-    uint32_t offset, uint32_t* lineNum, uint32_t* column) const {
-  uint32_t lineIndex = lineIndexOf(offset);
-  *lineNum = lineIndexToNum(lineIndex);
-  *column = lineIndexAndOffsetToColumn(lineIndex, offset);
+TokenStreamAnyChars::SourceCoords::LineToken
+TokenStreamAnyChars::SourceCoords::lineToken(uint32_t offset) const {
+  return LineToken(indexFromOffset(offset), offset);
 }
 
 TokenStreamAnyChars::TokenStreamAnyChars(JSContext* cx,
                                          const ReadOnlyCompileOptions& options,
                                          StrictModeGetter* smg)
     : srcCoords(cx, options.lineno, options.column, options.scriptSourceOffset),
       options_(options),
       tokens(),
@@ -673,18 +665,18 @@ MOZ_COLD void TokenStreamChars<Utf8Unit,
 
   do {
     size_t offset = this->sourceUnits.offset();
 
     ErrorMetadata err;
 
     TokenStreamAnyChars& anyChars = anyCharsAccess();
 
-    bool hasLineOfContext = anyChars.fillExcludingContext(&err, offset);
-    if (hasLineOfContext) {
+    bool canAddLineOfContext = fillExceptingContext(&err, offset);
+    if (canAddLineOfContext) {
       if (!internalComputeLineOfContext(&err, offset)) {
         break;
       }
 
       // As this is an encoding error, the computed window-end must be
       // identical to the location of the error -- any further on and the
       // window would contain invalid Unicode.
       MOZ_ASSERT_IF(err.lineOfContext != nullptr,
@@ -712,17 +704,17 @@ MOZ_COLD void TokenStreamChars<Utf8Unit,
 
       ptr += 5;
       relevantUnits--;
     }
 
     ptr[-1] = '\0';
 
     uint32_t line, column;
-    anyChars.srcCoords.lineNumAndColumnIndex(offset, &line, &column);
+    computeLineAndColumn(offset, &line, &column);
 
     if (!notes->addNoteASCII(anyChars.cx, anyChars.getFilename(), line, column,
                              GetErrorMessage, nullptr, JSMSG_BAD_CODE_UNITS,
                              badUnitsStr)) {
       break;
     }
 
     ReportCompileError(anyChars.cx, std::move(err), std::move(notes),
@@ -1313,17 +1305,17 @@ void TokenStreamAnyChars::computeErrorMe
   err->isMuted = mutedErrors;
   err->filename = filename_;
   err->lineNumber = 0;
   err->columnNumber = 0;
 
   MOZ_ASSERT(err->lineOfContext == nullptr);
 }
 
-bool TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err,
+bool TokenStreamAnyChars::fillExceptingContext(ErrorMetadata* err,
                                                uint32_t offset) {
   err->isMuted = mutedErrors;
 
   // If this TokenStreamAnyChars doesn't have location information, try to
   // get it from the caller.
   if (!filename_ && !cx->helperThread()) {
     NonBuiltinFrameIter iter(cx, FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
                              cx->realm()->principals());
@@ -1331,39 +1323,25 @@ bool TokenStreamAnyChars::fillExcludingC
       err->filename = iter.filename();
       err->lineNumber = iter.computeLine(&err->columnNumber);
       return false;
     }
   }
 
   // Otherwise use this TokenStreamAnyChars's location information.
   err->filename = filename_;
-  srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
   return true;
 }
 
 template <typename Unit, class AnyCharsAccess>
 bool TokenStreamSpecific<Unit, AnyCharsAccess>::hasTokenizationStarted() const {
   const TokenStreamAnyChars& anyChars = anyCharsAccess();
   return anyChars.isCurrentTokenType(TokenKind::Eof) && !anyChars.isEOF();
 }
 
-void TokenStreamAnyChars::lineAndColumnAt(size_t offset, uint32_t* line,
-                                          uint32_t* column) const {
-  srcCoords.lineNumAndColumnIndex(offset, line, column);
-}
-
-template <typename Unit, class AnyCharsAccess>
-void TokenStreamSpecific<Unit, AnyCharsAccess>::currentLineAndColumn(
-    uint32_t* line, uint32_t* column) const {
-  const TokenStreamAnyChars& anyChars = anyCharsAccess();
-  uint32_t offset = anyChars.currentToken().pos.begin;
-  anyChars.srcCoords.lineNumAndColumnIndex(offset, line, column);
-}
-
 template <>
 inline void SourceUnits<char16_t>::computeWindowOffsetAndLength(
     const char16_t* encodedWindow, size_t encodedTokenOffset,
     size_t* utf16TokenOffset, size_t encodedWindowLength,
     size_t* utf16WindowLength) {
   MOZ_ASSERT_UNREACHABLE("shouldn't need to recompute for UTF-16");
 }
 
@@ -1502,25 +1480,25 @@ template <typename Unit, class AnyCharsA
 bool TokenStreamSpecific<Unit, AnyCharsAccess>::computeErrorMetadata(
     ErrorMetadata* err, uint32_t offset) {
   if (offset == NoOffset) {
     anyCharsAccess().computeErrorMetadataNoOffset(err);
     return true;
   }
 
   // This function's return value isn't a success/failure indication: it
-  // returns true if this TokenStream's location information could be used,
-  // and it returns false when that information can't be used (and so we
-  // can't provide a line of context).
-  if (!anyCharsAccess().fillExcludingContext(err, offset)) {
-    return true;
+  // returns true if this TokenStream can be used to provide a line of
+  // context.
+  if (fillExceptingContext(err, offset)) {
+    // Add a line of context from this TokenStream to help with debugging.
+    return internalComputeLineOfContext(err, offset);
   }
 
-  // Add a line of context from this TokenStream to help with debugging.
-  return internalComputeLineOfContext(err, offset);
+  // We can't fill in any more here.
+  return true;
 }
 
 template <typename Unit, class AnyCharsAccess>
 bool TokenStreamSpecific<Unit, AnyCharsAccess>::reportStrictModeError(
     unsigned errorNumber, ...) {
   va_list args;
   va_start(args, errorNumber);
 
@@ -3504,19 +3482,31 @@ const char* TokenKindToString(TokenKind 
 
   return "<bad TokenKind>";
 }
 #endif
 
 template class TokenStreamCharsBase<Utf8Unit>;
 template class TokenStreamCharsBase<char16_t>;
 
+template class GeneralTokenStreamChars<char16_t, TokenStreamAnyCharsAccess>;
 template class TokenStreamChars<char16_t, TokenStreamAnyCharsAccess>;
 template class TokenStreamSpecific<char16_t, TokenStreamAnyCharsAccess>;
 
+template class GeneralTokenStreamChars<
+    Utf8Unit, ParserAnyCharsAccess<GeneralParser<FullParseHandler, Utf8Unit>>>;
+template class GeneralTokenStreamChars<
+    Utf8Unit,
+    ParserAnyCharsAccess<GeneralParser<SyntaxParseHandler, Utf8Unit>>>;
+template class GeneralTokenStreamChars<
+    char16_t, ParserAnyCharsAccess<GeneralParser<FullParseHandler, char16_t>>>;
+template class GeneralTokenStreamChars<
+    char16_t,
+    ParserAnyCharsAccess<GeneralParser<SyntaxParseHandler, char16_t>>>;
+
 template class TokenStreamChars<
     Utf8Unit, ParserAnyCharsAccess<GeneralParser<FullParseHandler, Utf8Unit>>>;
 template class TokenStreamChars<
     Utf8Unit,
     ParserAnyCharsAccess<GeneralParser<SyntaxParseHandler, Utf8Unit>>>;
 template class TokenStreamChars<
     char16_t, ParserAnyCharsAccess<GeneralParser<FullParseHandler, char16_t>>>;
 template class TokenStreamChars<
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -218,16 +218,18 @@ struct JSContext;
 struct KeywordInfo;
 
 namespace js {
 
 class AutoKeepAtoms;
 
 namespace frontend {
 
+class FunctionBox;
+
 struct TokenPos {
   uint32_t begin;  // Offset of the token's first code unit.
   uint32_t end;    // Offset of 1 past the token's last code unit.
 
   TokenPos() : begin(0), end(0) {}
   TokenPos(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
 
   // Return a TokenPos that covers left, right, and anything in between.
@@ -724,114 +726,224 @@ class TokenStreamAnyChars : public Token
 
   char16_t* displayURL() { return displayURL_.get(); }
 
   bool hasSourceMapURL() const { return sourceMapURL_ != nullptr; }
 
   char16_t* sourceMapURL() { return sourceMapURL_.get(); }
 
   // This class maps a sourceUnits offset (which is 0-indexed) to a line
-  // number (which is 1-indexed) and a column index (which is 0-indexed).
+  // number (which is 1-indexed) and an offset (which is 0-indexed) in
+  // code *units* (not code points, not bytes) into the line.
   class SourceCoords {
     // For a given buffer holding source code, |lineStartOffsets_| has one
     // element per line of source code, plus one sentinel element.  Each
     // non-sentinel element holds the buffer offset for the start of the
     // corresponding line of source code.  For this example script,
     // assuming an initialLineOffset of 0:
     //
     // 1  // xyz            [line starts at offset 0]
     // 2  var x;            [line starts at offset 7]
     // 3                    [line starts at offset 14]
     // 4  var y;            [line starts at offset 15]
     //
     // |lineStartOffsets_| is:
     //
     //   [0, 7, 14, 15, MAX_PTR]
     //
-    // To convert a "line number" to a "line index" (i.e. an index into
-    // |lineStartOffsets_|), subtract |initialLineNum_|.  E.g. line 3's
-    // line index is (3 - initialLineNum_), which is 2.  Therefore
-    // lineStartOffsets_[2] holds the buffer offset for the start of line 3,
-    // which is 14.  (Note that |initialLineNum_| is often 1, but not
-    // always.)
+    // To convert a "line number" to an "index" into |lineStartOffsets_|,
+    // subtract |initialLineNum_|.  E.g. line 3's index is
+    // (3 - initialLineNum_), which is 2.  Therefore lineStartOffsets_[2]
+    // holds the buffer offset for the start of line 3, which is 14.  (Note
+    // that |initialLineNum_| is often 1, but not always.
     //
     // The first element is always initialLineOffset, passed to the
     // constructor, and the last element is always the MAX_PTR sentinel.
     //
-    // offset-to-line/column lookups are O(log n) in the worst case (binary
-    // search), but in practice they're heavily clustered and we do better
-    // than that by using the previous lookup's result (lastLineIndex_) as
-    // a starting point.
+    // Offset-to-{line,offset-into-line} lookups are O(log n) in the worst
+    // case (binary search), but in practice they're heavily clustered and
+    // we do better than that by using the previous lookup's result
+    // (lastIndex_) as a starting point.
     //
     // Checking if an offset lies within a particular line number
     // (isOnThisLine()) is O(1).
     //
     Vector<uint32_t, 128> lineStartOffsets_;
+
+    /** The line number on which the source text begins. */
     uint32_t initialLineNum_;
+
+    /** The column number at which the source text begins. */
     uint32_t initialColumn_;
 
-    // This is mutable because it's modified on every search, but that fact
-    // isn't visible outside this class.
-    mutable uint32_t lastLineIndex_;
-
-    uint32_t lineIndexOf(uint32_t offset) const;
+    /**
+     * The index corresponding to the last offset lookup -- used so that if
+     * offset lookups proceed in increasing order, and and the offset appears
+     * in the next couple lines from the last offset, we can avoid a full
+     * binary-search.
+     *
+     * This is mutable because it's modified on every search, but that fact
+     * isn't visible outside this class.
+     */
+    mutable uint32_t lastIndex_;
+
+    uint32_t indexFromOffset(uint32_t offset) const;
 
     static const uint32_t MAX_PTR = UINT32_MAX;
 
-    uint32_t lineIndexToNum(uint32_t lineIndex) const {
-      return lineIndex + initialLineNum_;
+    uint32_t lineNumberFromIndex(uint32_t index) const {
+      return index + initialLineNum_;
     }
-    uint32_t lineNumToIndex(uint32_t lineNum) const {
+
+    uint32_t indexFromLineNumber(uint32_t lineNum) const {
       return lineNum - initialLineNum_;
     }
-    uint32_t lineIndexAndOffsetToColumn(uint32_t lineIndex,
-                                        uint32_t offset) const {
-      uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
+
+    uint32_t lineOffsetFromIndexAndOffset(uint32_t index,
+                                          uint32_t offset) const {
+      uint32_t lineStartOffset = lineStartOffsets_[index];
       MOZ_RELEASE_ASSERT(offset >= lineStartOffset);
-      uint32_t column = offset - lineStartOffset;
-      if (lineIndex == 0) {
-        return column + initialColumn_;
-      }
-      return column;
+      return offset - lineStartOffset;
+    }
+
+    // This function is BAD because it's lies in the presence of multi-unit
+    // code points -- unlike lineOffsetFromIndexAndOffset that doesn't
+    // promise a column number.  This name segregates the initial-line
+    // column adjustment and the false "column" sense to a function that
+    // will be removed later in this patch stack.
+    uint32_t columnFromIndexAndOffset(uint32_t index, uint32_t offset) const {
+      uint32_t lineOffset = lineOffsetFromIndexAndOffset(index, offset);
+      return (index == 0 ? initialColumn_ : 0) + lineOffset;
     }
 
    public:
-    SourceCoords(JSContext* cx, uint32_t ln, uint32_t col,
-                 uint32_t initialLineOffset);
+    SourceCoords(JSContext* cx, uint32_t initialLineNumber,
+                 uint32_t initialColumnNumber, uint32_t initialOffset);
 
     MOZ_MUST_USE bool add(uint32_t lineNum, uint32_t lineStartOffset);
     MOZ_MUST_USE bool fill(const SourceCoords& other);
 
     bool isOnThisLine(uint32_t offset, uint32_t lineNum,
                       bool* onThisLine) const {
-      uint32_t lineIndex = lineNumToIndex(lineNum);
-      if (lineIndex + 1 >= lineStartOffsets_.length()) {  // +1 due to sentinel
+      uint32_t index = indexFromLineNumber(lineNum);
+      if (index + 1 >= lineStartOffsets_.length()) {  // +1 due to sentinel
         return false;
       }
-      *onThisLine = lineStartOffsets_[lineIndex] <= offset &&
-                    offset < lineStartOffsets_[lineIndex + 1];
+      *onThisLine = lineStartOffsets_[index] <= offset &&
+                    offset < lineStartOffsets_[index + 1];
       return true;
     }
 
-    uint32_t lineNum(uint32_t offset) const;
-    uint32_t columnIndex(uint32_t offset) const;
-    void lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
-                               uint32_t* column) const;
+    /**
+     * A token, computed for an offset in source text, that can be used to
+     * access line number and line-offset information for that offset.
+     *
+     * LineToken *alone* exposes whether the corresponding offset is in the
+     * the first line of source (which may not be 1, depending on
+     * |initialLineNumber|), and whether it's in the same line as
+     * another LineToken.
+     */
+    class LineToken {
+      uint32_t index;
+#ifdef DEBUG
+      uint32_t offset_;  // stored for consistency-of-use assertions
+#endif
+
+      friend class SourceCoords;
+
+     public:
+      explicit LineToken(uint32_t index, uint32_t offset)
+          : index(index)
+#ifdef DEBUG
+            ,
+            offset_(offset)
+#endif
+      {
+      }
+
+      bool isFirstLine() const { return index == 0; }
+
+      bool isSameLine(LineToken other) const { return index == other.index; }
+    };
+
+    /**
+     * Compute a token usable to access information about the line at the
+     * given offset.
+     *
+     * The only information directly accessible in a token is whether it
+     * corresponds to the first line of source text (which may not be line
+     * 1, depending on the |initialLineNumber| value used to construct
+     * this).  Use |lineNumber(LineToken)| to compute the actual line
+     * number (incorporating the contribution of |initialLineNumber|).
+     */
+    LineToken lineToken(uint32_t offset) const;
+
+    /** Compute the line number for the given token. */
+    uint32_t lineNumber(LineToken lineToken) const {
+      return lineNumberFromIndex(lineToken.index);
+    }
+
+    /**
+     * Compute the offset *in code units* of |offset| from the start of the
+     * line containing it, plus any contribution from |initialColumnNumber|
+     * passed to the |SourceCoords| constructor.
+     *
+     * This is only really a "column".  A subsequent patch in this stack
+     * removes it, computing a multi-unit-aware column number elsewhere in
+     * Unit-sensitive manner.
+     */
+    uint32_t columnIndex(LineToken lineToken, uint32_t offset) const {
+      MOZ_ASSERT(lineToken.offset_ == offset, "use a consistent token");
+
+      uint32_t lineStartOffset = lineStartOffsets_[lineToken.index];
+      MOZ_RELEASE_ASSERT(offset >= lineStartOffset);
+
+      uint32_t relative = offset - lineStartOffset;
+      return (lineToken.isFirstLine() ? initialColumn_ : 0) + relative;
+    }
   };
 
   SourceCoords srcCoords;
 
   JSContext* context() const { return cx; }
 
+  using LineToken = SourceCoords::LineToken;
+
+  LineToken lineToken(uint32_t offset) const {
+    return srcCoords.lineToken(offset);
+  }
+
+  uint32_t lineNumber(LineToken lineToken) const {
+    return srcCoords.lineNumber(lineToken);
+  }
+
+  uint32_t columnIndex(LineToken lineToken, uint32_t offset) const {
+    return srcCoords.columnIndex(lineToken, offset);
+  }
+
+  // A helper function if you want an offset's line *and* "column" info.
+  void lineAndColumnAt(uint32_t offset, uint32_t* lineNum,
+                       uint32_t* column) const {
+    LineToken token = srcCoords.lineToken(offset);
+    *lineNum = srcCoords.lineNumber(token);
+    *column = srcCoords.columnIndex(token, offset);
+  }
+
   /**
-   * Fill in |err|, excepting line-of-context-related fields.  If the token
-   * stream has location information, use that and return true.  If it does
-   * not, use the caller's location information and return false.
+   * Fill in |err|.
+   *
+   * If the token stream doesn't have location info for this error, use the
+   * caller's location (including line/column number) and return false.  (No
+   * line of context is set.)
+   *
+   * Otherwise fill in everything in |err| except 1) line/column numbers and
+   * 2) line-of-context-related fields and return true.  The caller *must*
+   * fill in the line/column number; filling the line of context is optional.
    */
-  bool fillExcludingContext(ErrorMetadata* err, uint32_t offset);
+  bool fillExceptingContext(ErrorMetadata* err, uint32_t offset);
 
   MOZ_ALWAYS_INLINE void updateFlagsForEOL() { flags.isDirtyLine = false; }
 
  private:
   MOZ_MUST_USE MOZ_ALWAYS_INLINE bool internalUpdateLineInfoForEOL(
       uint32_t lineStartOffset);
 
   void undoInternalUpdateLineInfoForEOL();
@@ -870,18 +982,16 @@ class TokenStreamAnyChars : public Token
                                    unsigned flags, unsigned errorNumber,
                                    va_list args);
 
   // Compute error metadata for an error at no offset.
   void computeErrorMetadataNoOffset(ErrorMetadata* err);
 
   // ErrorReporter API Helpers
 
-  void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const;
-
   // This is just straight up duplicated from TokenStreamSpecific's inheritance
   // of ErrorReporter's reportErrorNoOffset. varargs delenda est.
   void reportErrorNoOffset(unsigned errorNumber, ...);
   void reportErrorNoOffsetVA(unsigned errorNumber, va_list args);
 
   const JS::ReadOnlyCompileOptions& options() const { return options_; }
 
   const char* getFilename() const { return filename_; }
@@ -1853,16 +1963,35 @@ class GeneralTokenStreamChars : public S
   TokenStreamSpecific* asSpecific() {
     static_assert(
         mozilla::IsBaseOf<GeneralTokenStreamChars, TokenStreamSpecific>::value,
         "static_cast below presumes an inheritance relationship");
 
     return static_cast<TokenStreamSpecific*>(this);
   }
 
+  void computeLineAndColumn(uint32_t offset, uint32_t* line,
+                            uint32_t* column) const {
+    anyCharsAccess().lineAndColumnAt(offset, line, column);
+  }
+
+  /**
+   * Fill in |err| completely, except for line-of-context information.
+   *
+   * Return true if the caller can compute a line of context from the token
+   * stream.  Otherwise return false.
+   */
+  MOZ_MUST_USE bool fillExceptingContext(ErrorMetadata* err, uint32_t offset) {
+    if (anyCharsAccess().fillExceptingContext(err, offset)) {
+      computeLineAndColumn(offset, &err->lineNumber, &err->columnNumber);
+      return true;
+    }
+    return false;
+  }
+
   void newSimpleToken(TokenKind kind, TokenStart start,
                       TokenStreamShared::Modifier modifier, TokenKind* out) {
     newToken(kind, start, modifier, out);
   }
 
   void newNumberToken(double dval, DecimalPoint decimalPoint, TokenStart start,
                       TokenStreamShared::Modifier modifier, TokenKind* out) {
     Token* token = newToken(TokenKind::Number, start, modifier, out);
@@ -2018,16 +2147,18 @@ class GeneralTokenStreamChars : public S
     // separators don't need special handling.
     // https://tc39.github.io/ecma262/#sec-static-semantics-tv-and-trv
     if (!fillCharBufferFromSourceNormalizingAsciiLineBreaks(cur, end)) {
       return nullptr;
     }
 
     return drainCharBufferIntoAtom(anyChars.cx);
   }
+
+  inline void setFunctionStart(FunctionBox* funbox) const;
 };
 
 template <typename Unit, class AnyCharsAccess>
 class TokenStreamChars;
 
 template <class AnyCharsAccess>
 class TokenStreamChars<char16_t, AnyCharsAccess>
     : public GeneralTokenStreamChars<char16_t, AnyCharsAccess> {
@@ -2090,16 +2221,18 @@ class TokenStreamChars<mozilla::Utf8Unit
       GeneralTokenStreamChars<mozilla::Utf8Unit, AnyCharsAccess>;
   using Self = TokenStreamChars<mozilla::Utf8Unit, AnyCharsAccess>;
 
   using typename SpecializedCharsBase::SourceUnitsEnd;
   using typename SpecializedCharsBase::SourceUnitsIterator;
 
  protected:
   using GeneralCharsBase::anyCharsAccess;
+  using GeneralCharsBase::computeLineAndColumn;
+  using GeneralCharsBase::fillExceptingContext;
   using GeneralCharsBase::internalComputeLineOfContext;
   using TokenStreamCharsShared::isAsciiCodePoint;
   // Deliberately don't |using| |sourceUnits| because of bug 1472569.  :-(
   using GeneralCharsBase::updateLineInfoForEOL;
 
  private:
   static char toHexChar(uint8_t nibble) {
     MOZ_ASSERT(nibble < 16);
@@ -2288,29 +2421,31 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
   // onerous.
   //
   // As an alternative, we directly add every one of these functions to this
   // class, using explicit qualification to address the dependent-name
   // problem.  |this| or other qualification is no longer necessary -- at
   // cost of this ever-changing laundry list of |using|s.  So it goes.
  public:
   using GeneralCharsBase::anyCharsAccess;
+  using GeneralCharsBase::computeLineAndColumn;
 
  private:
   using typename CharsBase::SourceUnits;
 
  private:
   using CharsBase::atomizeSourceChars;
   using GeneralCharsBase::badToken;
   using TokenStreamCharsShared::appendCodePointToCharBuffer;
   // Deliberately don't |using| |charBuffer| because of bug 1472569.  :-(
   using CharsBase::consumeKnownCodeUnit;
   using CharsBase::fillCharBufferFromSourceNormalizingAsciiLineBreaks;
   using CharsBase::matchCodeUnit;
   using CharsBase::matchLineTerminator;
+  using GeneralCharsBase::fillExceptingContext;
   using GeneralCharsBase::getCodeUnit;
   using GeneralCharsBase::getFullAsciiCodePoint;
   using GeneralCharsBase::internalComputeLineOfContext;
   using GeneralCharsBase::matchUnicodeEscapeIdent;
   using GeneralCharsBase::matchUnicodeEscapeIdStart;
   using GeneralCharsBase::newAtomToken;
   using GeneralCharsBase::newNameToken;
   using GeneralCharsBase::newNumberToken;
@@ -2364,27 +2499,36 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     return anyCharsAccess().options();
   }
 
   void lineAndColumnAt(size_t offset, uint32_t* line,
                        uint32_t* column) const final {
     anyCharsAccess().lineAndColumnAt(offset, line, column);
   }
 
-  void currentLineAndColumn(uint32_t* line, uint32_t* column) const final;
+  void currentLineAndColumn(uint32_t* line, uint32_t* column) const final {
+    computeLineAndColumn(anyCharsAccess().currentToken().pos.begin, line,
+                         column);
+  }
 
   bool isOnThisLine(size_t offset, uint32_t lineNum,
                     bool* onThisLine) const final {
     return anyCharsAccess().srcCoords.isOnThisLine(offset, lineNum, onThisLine);
   }
+
   uint32_t lineAt(size_t offset) const final {
-    return anyCharsAccess().srcCoords.lineNum(offset);
+    const auto& anyChars = anyCharsAccess();
+    auto lineToken = anyChars.lineToken(offset);
+    return anyChars.lineNumber(lineToken);
   }
+
   uint32_t columnAt(size_t offset) const final {
-    return anyCharsAccess().srcCoords.columnIndex(offset);
+    const TokenStreamAnyChars& anyChars = anyCharsAccess();
+    auto lineToken = anyChars.lineToken(offset);
+    return anyChars.columnIndex(lineToken, offset);
   }
 
   bool hasTokenizationStarted() const final;
 
   void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) final {
     anyCharsAccess().reportErrorNoOffsetVA(errorNumber, args);
   }
 
@@ -2601,23 +2745,28 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     // - The next token starts on the same line, but lookahead==2 and there
     //   is a newline between the next token and the one after that.
     // The following test is somewhat expensive but gets these cases (and
     // all others) right.
     TokenKind tmp;
     if (!getToken(&tmp, modifier)) {
       return false;
     }
+
     const Token& next = anyChars.currentToken();
     anyChars.ungetToken();
 
-    const auto& srcCoords = anyChars.srcCoords;
-    *ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
-               ? next.type
-               : TokenKind::Eol;
+    // Careful, |next| points to an initialized-but-not-allocated Token!
+    // This is safe because we don't modify token data below.
+
+    auto currentEndToken = anyChars.lineToken(curr.pos.end);
+    auto nextBeginToken = anyChars.lineToken(next.pos.begin);
+
+    *ttp =
+        currentEndToken.isSameLine(nextBeginToken) ? next.type : TokenKind::Eol;
     return true;
   }
 
   // Get the next token from the stream if its kind is |tt|.
   MOZ_MUST_USE bool matchToken(bool* matchedp, TokenKind tt,
                                Modifier modifier = None) {
     TokenKind token;
     if (!getToken(&token, modifier)) {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -156,20 +156,20 @@
  * in the background, are swept on the background thread. This accounts for most
  * of the sweeping work.
  *
  * Reset
  * -----
  *
  * During incremental collection it is possible, although unlikely, for
  * conditions to change such that incremental collection is no longer safe. In
- * this case, the collection is 'reset' by ResetIncrementalGC(). If we are in
+ * this case, the collection is 'reset' by resetIncrementalGC(). If we are in
  * the mark state, this just stops marking, but if we have started sweeping
- * already, we continue until we have swept the current sweep group. Following a
- * reset, a new non-incremental collection is started.
+ * already, we continue non-incrementally until we have swept the current sweep
+ * group. Following a reset, a new collection is started.
  *
  * Compacting GC
  * -------------
  *
  * Compacting GC happens at the end of a major GC as part of the last slice.
  * There are three parts:
  *
  *  - Arenas are selected for compaction.
@@ -4954,31 +4954,32 @@ static void ResetGrayList(Compartment* c
 void GCRuntime::getNextSweepGroup() {
   currentSweepGroup = currentSweepGroup->nextGroup();
   ++sweepGroupIndex;
   if (!currentSweepGroup) {
     abortSweepAfterCurrentGroup = false;
     return;
   }
 
+  MOZ_ASSERT_IF(abortSweepAfterCurrentGroup, !isIncremental);
+  if (!isIncremental) {
+    ZoneComponentFinder::mergeGroups(currentSweepGroup);
+  }
+
   for (Zone* zone = currentSweepGroup; zone; zone = zone->nextNodeInGroup()) {
     MOZ_ASSERT(zone->isGCMarking());
     MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
   }
 
-  if (!isIncremental) {
-    ZoneComponentFinder::mergeGroups(currentSweepGroup);
-  }
-
   if (abortSweepAfterCurrentGroup) {
-    MOZ_ASSERT(!isIncremental);
     for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
       MOZ_ASSERT(!zone->gcNextGraphComponent);
       zone->setNeedsIncrementalBarrier(false);
       zone->changeGCState(Zone::Mark, Zone::NoGC);
+      zone->arenas.unmarkPreMarkedFreeCells();
       zone->gcGrayRoots().clearAndFree();
     }
 
     for (SweepGroupCompartmentsIter comp(rt); !comp.done(); comp.next()) {
       ResetGrayList(comp);
     }
 
     abortSweepAfterCurrentGroup = false;
@@ -6658,18 +6659,19 @@ void GCRuntime::finishCollection() {
   schedulingState.updateHighFrequencyMode(lastGCTime, currentTime, tunables);
 
   for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
     if (zone->isCollecting()) {
       zone->changeGCState(Zone::Finished, Zone::NoGC);
       zone->notifyObservingDebuggers();
     }
 
-    MOZ_ASSERT(!zone->isCollectingFromAnyThread());
     MOZ_ASSERT(!zone->wasGCStarted());
+    MOZ_ASSERT(!zone->needsIncrementalBarrier());
+    MOZ_ASSERT(!zone->isOnList());
   }
 
   MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty());
 
   lastGCTime = currentTime;
 }
 
 static const char* HeapStateToLabel(JS::HeapState heapState) {
@@ -6710,34 +6712,34 @@ AutoHeapSession::~AutoHeapSession() {
 }
 
 JS_PUBLIC_API JS::HeapState JS::RuntimeHeapState() {
   return TlsContext.get()->runtime()->heapState();
 }
 
 GCRuntime::IncrementalResult GCRuntime::resetIncrementalGC(
     gc::AbortReason reason) {
+  // Drop as much work as possible from an ongoing incremental GC so
+  // we can start a new GC after it has finished.
   if (incrementalState == State::NotActive) {
     return IncrementalResult::Ok;
   }
 
-  minorGC(JS::gcreason::RESET, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
-
   AutoGCSession session(rt, JS::HeapState::MajorCollecting);
 
   switch (incrementalState) {
     case State::NotActive:
     case State::MarkRoots:
+    case State::Finish:
       MOZ_CRASH("Unexpected GC state in resetIncrementalGC");
       break;
 
     case State::Mark: {
-      /* Cancel any ongoing marking. */
+      // Cancel any ongoing marking.
       marker.reset();
-      marker.stop();
       clearBufferedGrayRoots();
 
       for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         ResetGrayList(c);
       }
 
       for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         zone->setNeedsIncrementalBarrier(false);
@@ -6746,108 +6748,57 @@ GCRuntime::IncrementalResult GCRuntime::
       }
 
       {
         AutoLockHelperThreadState lock;
         blocksToFreeAfterSweeping.ref().freeAll();
       }
 
       lastMarkSlice = false;
-      incrementalState = State::NotActive;
+      incrementalState = State::Finish;
 
       MOZ_ASSERT(!marker.shouldCheckCompartments());
 
       break;
     }
 
     case State::Sweep: {
+      // Finish sweeping the current sweep group, then abort.
       marker.reset();
 
       for (CompartmentsIter c(rt); !c.done(); c.next()) {
         c->gcState.scheduledForDestruction = false;
       }
 
-      for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        if (zone->isGCMarking()) {
-          zone->arenas.unmarkPreMarkedFreeCells();
-        }
-      }
-
-      /* Finish sweeping the current sweep group, then abort. */
       abortSweepAfterCurrentGroup = true;
-
-      /* Don't perform any compaction after sweeping. */
-      bool wasCompacting = isCompacting;
       isCompacting = false;
 
-      auto unlimited = SliceBudget::unlimited();
-      incrementalSlice(unlimited, JS::gcreason::RESET, session);
-
-      isCompacting = wasCompacting;
-
-      {
-        gcstats::AutoPhase ap(stats(),
-                              gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
-        waitBackgroundSweepOrAllocEnd();
-      }
       break;
     }
 
     case State::Finalize: {
-      {
-        gcstats::AutoPhase ap(stats(),
-                              gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
-        waitBackgroundSweepOrAllocEnd();
-      }
-
-      bool wasCompacting = isCompacting;
       isCompacting = false;
-
-      auto unlimited = SliceBudget::unlimited();
-      incrementalSlice(unlimited, JS::gcreason::RESET, session);
-
-      isCompacting = wasCompacting;
-
       break;
     }
 
     case State::Compact: {
-      bool wasCompacting = isCompacting;
-
-      isCompacting = true;
+      // Skip any remaining zones that would have been compacted.
+      MOZ_ASSERT(isCompacting);
       startedCompacting = true;
       zonesToMaybeCompact.ref().clear();
-
-      auto unlimited = SliceBudget::unlimited();
-      incrementalSlice(unlimited, JS::gcreason::RESET, session);
-
-      isCompacting = wasCompacting;
       break;
     }
 
     case State::Decommit: {
-      auto unlimited = SliceBudget::unlimited();
-      incrementalSlice(unlimited, JS::gcreason::RESET, session);
       break;
     }
   }
 
   stats().reset(reason);
 
-#ifdef DEBUG
-  assertBackgroundSweepingFinished();
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
-    MOZ_ASSERT(!zone->isCollectingFromAnyThread());
-    MOZ_ASSERT(!zone->needsIncrementalBarrier());
-    MOZ_ASSERT(!zone->isOnList());
-  }
-  MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty());
-  MOZ_ASSERT(incrementalState == State::NotActive);
-#endif
-
   return IncrementalResult::ResetIncremental;
 }
 
 namespace {
 
 /*
  * Temporarily disable barriers during GC slices.
  */
@@ -6907,17 +6858,17 @@ static bool IsShutdownGC(JS::gcreason::R
 static bool ShouldCleanUpEverything(JS::gcreason::Reason reason,
                                     JSGCInvocationKind gckind) {
   // During shutdown, we must clean everything up, for the sake of leak
   // detection. When a runtime has no contexts, or we're doing a GC before a
   // shutdown CC, those are strong indications that we're shutting down.
   return IsShutdownGC(reason) || gckind == GC_SHRINK;
 }
 
-GCRuntime::IncrementalResult GCRuntime::incrementalSlice(
+void GCRuntime::incrementalSlice(
     SliceBudget& budget, JS::gcreason::Reason reason, AutoGCSession& session) {
   AutoDisableBarriers disableBarriers(rt);
 
   bool destroyingRuntime = (reason == JS::gcreason::DESTROY_RUNTIME);
 
   number++;
 
   initialState = incrementalState;
@@ -6974,17 +6925,17 @@ GCRuntime::IncrementalResult GCRuntime::
 
       incrementalState = State::MarkRoots;
 
       MOZ_FALLTHROUGH;
 
     case State::MarkRoots:
       if (!beginMarkPhase(reason, session)) {
         incrementalState = State::NotActive;
-        return IncrementalResult::Ok;
+        return;
       }
 
       /* If we needed delayed marking for gray roots, then collect until done.
        */
       if (isIncremental && !hasValidGrayRootsBuffer()) {
         budget.makeUnlimited();
         isIncremental = false;
         stats().nonincremental(AbortReason::GrayRootBufferingFailed);
@@ -7058,30 +7009,31 @@ GCRuntime::IncrementalResult GCRuntime::
       }
 
       endSweepPhase(destroyingRuntime);
 
       incrementalState = State::Finalize;
 
       MOZ_FALLTHROUGH;
 
-    case State::Finalize: {
-      gcstats::AutoPhase ap(stats(),
-                            gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
-
-      // Yield until background finalization is done.
-      if (!budget.isUnlimited()) {
-        // Poll for end of background sweeping
-        if (isBackgroundSweeping()) {
-          break;
+    case State::Finalize:
+      {
+        gcstats::AutoPhase ap(stats(),
+                              gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
+
+        // Yield until background finalization is done.
+        if (!budget.isUnlimited()) {
+          // Poll for end of background sweeping
+          if (isBackgroundSweeping()) {
+            break;
+          }
+        } else {
+          waitBackgroundSweepEnd();
         }
-      } else {
-        waitBackgroundSweepEnd();
       }
-    }
 
       {
         // Re-sweep the zones list, now that background finalization is
         // finished to actually remove and free dead zones.
         gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::SWEEP);
         gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::DESTROY);
         AutoSetThreadIsSweeping threadIsSweeping;
         FreeOp fop(rt);
@@ -7125,26 +7077,29 @@ GCRuntime::IncrementalResult GCRuntime::
                             gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
 
       // Yield until background decommit is done.
       if (!budget.isUnlimited() && decommitTask.isRunning()) {
         break;
       }
 
       decommitTask.join();
-    }
-
+
+      incrementalState = State::Finish;
+
+      MOZ_FALLTHROUGH;
+    }
+
+    case State::Finish:
       finishCollection();
       incrementalState = State::NotActive;
       break;
   }
 
   MOZ_ASSERT(safeToYield);
-
-  return IncrementalResult::Ok;
 }
 
 gc::AbortReason gc::IsIncrementalGCUnsafe(JSRuntime* rt) {
   MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
   if (!rt->gc.isIncrementalGCAllowed()) {
     return gc::AbortReason::IncrementalDisabled;
   }
@@ -7238,16 +7193,17 @@ GCRuntime::IncrementalResult GCRuntime::
 
     if (isIncrementalGCInProgress() &&
         zone->isGCScheduled() != zone->wasGCStarted()) {
       reset = true;
     }
   }
 
   if (reset) {
+    budget.makeUnlimited();
     return resetIncrementalGC(AbortReason::ZoneChange);
   }
 
   return IncrementalResult::Ok;
 }
 
 namespace {
 
@@ -7336,37 +7292,34 @@ void GCRuntime::maybeCallGCCallback(JSGC
 }
 
 /*
  * We disable inlining to ensure that the bottom of the stack with possible GC
  * roots recorded in MarkRuntime excludes any pointers we use during the marking
  * implementation.
  */
 MOZ_NEVER_INLINE GCRuntime::IncrementalResult GCRuntime::gcCycle(
-    bool nonincrementalByAPI, SliceBudget& budget,
+    bool nonincrementalByAPI, SliceBudget budget,
     JS::gcreason::Reason reason) {
   // Assert if this is a GC unsafe region.
   rt->mainContextFromOwnThread()->verifyIsSafeToGC();
 
   // It's ok if threads other than the main thread have suppressGC set, as
   // they are operating on zones which will not be collected from here.
   MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
   // Note that GC callbacks are allowed to re-enter GC.
   AutoCallGCCallbacks callCallbacks(*this);
 
   gcstats::AutoGCSlice agc(stats(), scanZonesBeforeGC(), invocationKind, budget,
                            reason);
 
   auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget);
-
-  // If an ongoing incremental GC was reset, we may need to restart.
   if (result == IncrementalResult::ResetIncremental) {
-    MOZ_ASSERT(!isIncrementalGCInProgress());
-    return result;
+    reason = JS::gcreason::RESET;
   }
 
   if (shouldCollectNurseryForSlice(nonincrementalByAPI, budget)) {
     minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
   }
 
   AutoGCSession session(rt, JS::HeapState::MajorCollecting);
 
@@ -7392,27 +7345,30 @@ MOZ_NEVER_INLINE GCRuntime::IncrementalR
   // We don't allow off-thread parsing to start while we're doing an
   // incremental GC of the atoms zone.
   if (rt->activeGCInAtomsZone()) {
     session.maybeCheckAtomsAccess.emplace(rt);
   }
 
   gcTracer.traceMajorGCStart();
 
-  result = incrementalSlice(budget, reason, session);
+  incrementalSlice(budget, reason, session);
 
   chunkAllocationSinceLastGC = false;
 
 #ifdef JS_GC_ZEAL
   /* Keeping these around after a GC is dangerous. */
   clearSelectedForMarking();
 #endif
 
   gcTracer.traceMajorGCEnd();
 
+  MOZ_ASSERT_IF(result == IncrementalResult::ResetIncremental,
+                !isIncrementalGCInProgress());
+
   return result;
 }
 
 bool GCRuntime::shouldCollectNurseryForSlice(bool nonincrementalByAPI,
                                              SliceBudget& budget) {
   if (!nursery().isEnabled()) {
     return false;
   }
@@ -7425,20 +7381,23 @@ bool GCRuntime::shouldCollectNurseryForS
     case State::Decommit:
       return true;
     case State::Mark:
       return (nonincrementalByAPI || budget.isUnlimited() || lastMarkSlice ||
               nursery().minorGCRequested() ||
               nursery().freeSpace() <
                   tunables.nurseryFreeThresholdForIdleCollection() ||
               hasIncrementalTwoSliceZealMode());
-    default:
-      // State::MarkRoots can't ever happen here.
-      MOZ_CRASH("Unhandled GC state");
-  }
+    case State::Finish:
+      return false;
+    case State::MarkRoots:
+      MOZ_CRASH("Unexpected GC state");
+  }
+
+  return false;
 }
 
 #ifdef JS_GC_ZEAL
 static bool IsDeterministicGCReason(JS::gcreason::Reason reason) {
   switch (reason) {
     case JS::gcreason::API:
     case JS::gcreason::DESTROY_RUNTIME:
     case JS::gcreason::LAST_DITCH:
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -22,17 +22,18 @@ enum class MarkColor : uint32_t { Black 
 // The phases of an incremental GC.
 #define GCSTATES(D) \
   D(NotActive)      \
   D(MarkRoots)      \
   D(Mark)           \
   D(Sweep)          \
   D(Finalize)       \
   D(Compact)        \
-  D(Decommit)
+  D(Decommit)       \
+  D(Finish)
 enum class State {
 #define MAKE_STATE(name) name,
   GCSTATES(MAKE_STATE)
 #undef MAKE_STATE
 };
 
 // Reasons we reset an ongoing incremental GC or perform a non-incremental GC.
 #define GC_ABORT_REASONS(D)     \
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -586,22 +586,22 @@ class GCRuntime {
    * non-incremental GC).
    *
    * Returns:
    *  * ResetIncremental if we "reset" an existing incremental GC, which would
    *    force us to run another cycle or
    *  * Ok otherwise.
    */
   MOZ_MUST_USE IncrementalResult gcCycle(bool nonincrementalByAPI,
-                                         SliceBudget& budget,
+                                         SliceBudget budget,
                                          JS::gcreason::Reason reason);
   bool shouldRepeatForDeadZone(JS::gcreason::Reason reason);
-  IncrementalResult incrementalSlice(SliceBudget& budget,
-                                     JS::gcreason::Reason reason,
-                                     AutoGCSession& session);
+  void incrementalSlice(SliceBudget& budget,
+                        JS::gcreason::Reason reason,
+                        AutoGCSession& session);
   MOZ_MUST_USE bool shouldCollectNurseryForSlice(bool nonincrementalByAPI,
                                                  SliceBudget& budget);
 
   friend class AutoCallGCCallbacks;
   void maybeCallGCCallback(JSGCStatus status);
 
   void pushZealSelectedObjects();
   void purgeRuntime();
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -309,41 +309,50 @@ static inline bool ShouldMarkCrossCompar
   TenuredCell& dst = dstCell->asTenured();
 
   JS::Zone* dstZone = dst.zone();
   if (!src->zone()->isGCMarking() && !dstZone->isGCMarking()) {
     return false;
   }
 
   if (color == MarkColor::Black) {
+    // Check our sweep groups are correct: we should never have to
+    // mark something in a zone that we have started sweeping.
+    MOZ_ASSERT_IF(!dst.isMarkedBlack(), !dstZone->isGCSweeping());
+
     /*
      * Having black->gray edges violates our promise to the cycle
      * collector. This can happen if we're collecting a compartment and it
      * has an edge to an uncollected compartment: it's possible that the
      * source and destination of the cross-compartment edge should be gray,
      * but the source was marked black by the write barrier.
      */
     if (dst.isMarkedGray()) {
       MOZ_ASSERT(!dstZone->isCollecting());
       UnmarkGrayGCThing(marker->runtime(),
                         JS::GCCellPtr(&dst, dst.getTraceKind()));
     }
+
     return dstZone->isGCMarking();
   } else {
+    // Check our sweep groups are correct as above.
+    MOZ_ASSERT_IF(!dst.isMarkedAny(), !dstZone->isGCSweeping());
+
     if (dstZone->isGCMarkingBlack()) {
       /*
        * The destination compartment is being not being marked gray now,
        * but it will be later, so record the cell so it can be marked gray
        * at the appropriate time.
        */
       if (!dst.isMarkedAny()) {
         DelayCrossCompartmentGrayMarking(src);
       }
       return false;
     }
+
     return dstZone->isGCMarkingGray();
   }
 }
 
 static bool ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src,
                                         Cell* dstCell) {
   if (!trc->isMarkingTracer()) {
     return true;
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -759,23 +759,23 @@ static JSObject* MaybeGetDelegate(Cell* 
   }
 
   JS::AutoSuppressGCAnalysis nogc; // Calling the delegate op cannot GC.
   return op(object);
 }
 
 bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key,
                                       Cell* value) {
-  Zone* zone = map->zone();
+  DebugOnly<Zone*> zone = map->zone();
 
   JSObject* object = map->memberOf;
   MOZ_ASSERT_IF(object, object->zone() == zone);
 
   // Debugger weak maps can have keys in different zones.
-  DebugOnly<Zone*> keyZone = GetCellZone(key);
+  Zone* keyZone = GetCellZone(key);
   MOZ_ASSERT_IF(!map->allowKeysInOtherZones(),
                 keyZone == zone || keyZone->isAtomsZone());
 
   DebugOnly<Zone*> valueZone = GetCellZone(value);
   MOZ_ASSERT(valueZone == zone || valueZone->isAtomsZone());
 
   // We may not know the color of the map, but we know that it's
   // alive so it must at least be marked gray.
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -32,16 +32,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Move.h"
 
 #include "frontend/TokenStream.h"
 #include "gc/GC.h"
 #include "irregexp/RegExpCharacters.h"
 #include "util/StringBuffer.h"
+#include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 
 using namespace js;
 using namespace js::irregexp;
 
 using mozilla::PointerRangeSize;
 
 // ----------------------------------------------------------------------------
@@ -258,17 +259,23 @@ RegExpParser<CharT>::RegExpParser(fronte
 }
 
 template <typename CharT>
 void
 RegExpParser<CharT>::SyntaxError(unsigned errorNumber, ...)
 {
     ErrorMetadata err;
 
-    ts.fillExcludingContext(&err, ts.currentToken().pos.begin);
+    // Ordinarily this indicates whether line-of-context information can be
+    // added, but we entirely ignore that here because we create a
+    // a line of context based on the expression source.
+    uint32_t location = ts.currentToken().pos.begin;
+    if (ts.fillExceptingContext(&err, location)) {
+        ts.lineAndColumnAt(location, &err.lineNumber, &err.columnNumber);
+    }
 
     // For most error reporting, the line of context derives from the token
     // stream.  So when location information doesn't come from the token
     // stream, we can't give a line of context.  But here the "line of context"
     // can be (and is) derived from the pattern text, so we can provide it no
     // matter if the location is derived from the caller.
     size_t offset = PointerRangeSize(start_, next_pos_ - 1);
     size_t end = PointerRangeSize(start_, end_);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4517,53 +4517,40 @@ extern JS_PUBLIC_API void JS_ScheduleGC(
 #endif
 
 extern JS_PUBLIC_API void JS_SetParallelParsingEnabled(JSContext* cx,
                                                        bool enabled);
 
 extern JS_PUBLIC_API void JS_SetOffthreadIonCompilationEnabled(JSContext* cx,
                                                                bool enabled);
 
-#define JIT_COMPILER_OPTIONS(Register)                                        \
-  Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") Register(      \
-      ION_WARMUP_TRIGGER, "ion.warmup.trigger") Register(ION_GVN_ENABLE,      \
-                                                         "ion.gvn.enable")    \
-      Register(ION_FORCE_IC, "ion.forceinlineCaches") Register(               \
-          ION_ENABLE, "ion.enable") Register(ION_CHECK_RANGE_ANALYSIS,        \
-                                             "ion.check-range-analysis")      \
-          Register(ION_FREQUENT_BAILOUT_THRESHOLD,                            \
-                   "ion.frequent-bailout-threshold")                          \
-              Register(BASELINE_ENABLE, "baseline.enable") Register(          \
-                  OFFTHREAD_COMPILATION_ENABLE,                               \
-                  "offthread-compilation.enable") Register(FULL_DEBUG_CHECKS, \
-                                                           "jit.full-debug-"  \
-                                                           "checks")          \
-                  Register(JUMP_THRESHOLD, "jump-threshold") Register(        \
-                      TRACK_OPTIMIZATIONS, "jit.track-optimizations")         \
-                      Register(SIMULATOR_ALWAYS_INTERRUPT,                    \
-                               "simulator.always-interrupt")                  \
-                          Register(SPECTRE_INDEX_MASKING,                     \
-                                   "spectre.index-masking")                   \
-                              Register(SPECTRE_OBJECT_MITIGATIONS_BARRIERS,   \
-                                       "spectre.object-mitigations.barriers") \
-                                  Register(SPECTRE_OBJECT_MITIGATIONS_MISC,   \
-                                           "spectre.object-mitigations.misc") \
-                                      Register(SPECTRE_STRING_MITIGATIONS,    \
-                                               "spectre.string-mitigations")  \
-                                          Register(SPECTRE_VALUE_MASKING,     \
-                                                   "spectre.value-masking")   \
-                                              Register(                       \
-                                                  SPECTRE_JIT_TO_CXX_CALLS,   \
-                                                  "spectre.jit-to-C++-calls") \
-                                                  Register(                   \
-                                                      WASM_FOLD_OFFSETS,      \
-                                                      "wasm.fold-offsets")    \
-                                                      Register(               \
-                                                          WASM_DELAY_TIER2,   \
-                                                          "wasm.delay-tier2")
+// clang-format off
+#define JIT_COMPILER_OPTIONS(Register) \
+  Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \
+  Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \
+  Register(ION_GVN_ENABLE, "ion.gvn.enable") \
+  Register(ION_FORCE_IC, "ion.forceinlineCaches") \
+  Register(ION_ENABLE, "ion.enable") \
+  Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \
+  Register(ION_FREQUENT_BAILOUT_THRESHOLD, "ion.frequent-bailout-threshold") \
+  Register(BASELINE_ENABLE, "baseline.enable") \
+  Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
+  Register(FULL_DEBUG_CHECKS, "jit.full-debug-checks") \
+  Register(JUMP_THRESHOLD, "jump-threshold") \
+  Register(TRACK_OPTIMIZATIONS, "jit.track-optimizations")\
+  Register(SIMULATOR_ALWAYS_INTERRUPT, "simulator.always-interrupt") \
+  Register(SPECTRE_INDEX_MASKING, "spectre.index-masking") \
+  Register(SPECTRE_OBJECT_MITIGATIONS_BARRIERS, "spectre.object-mitigations.barriers") \
+  Register(SPECTRE_OBJECT_MITIGATIONS_MISC, "spectre.object-mitigations.misc") \
+  Register(SPECTRE_STRING_MITIGATIONS, "spectre.string-mitigations") \
+  Register(SPECTRE_VALUE_MASKING, "spectre.value-masking") \
+  Register(SPECTRE_JIT_TO_CXX_CALLS, "spectre.jit-to-C++-calls") \
+  Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets") \
+  Register(WASM_DELAY_TIER2, "wasm.delay-tier2")
+// clang-format on
 
 typedef enum JSJitCompilerOption {
 #define JIT_COMPILER_DECLARE(key, str) JSJITCOMPILER_##key,
 
   JIT_COMPILER_OPTIONS(JIT_COMPILER_DECLARE)
 #undef JIT_COMPILER_DECLARE
 
       JSJITCOMPILER_NOT_AN_OPTION
--- a/js/src/util/Text.cpp
+++ b/js/src/util/Text.cpp
@@ -1,27 +1,39 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "util/Text.h"
 
+#include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/Utf8.h"
+
+#include <stddef.h>
+#include <stdint.h>
 
 #include "gc/GC.h"
 #include "js/GCAPI.h"
+#include "util/Unicode.h"
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 using namespace JS;
 using namespace js;
+
 using js::gc::AutoSuppressGC;
+using mozilla::DecodeOneUtf8CodePoint;
+using mozilla::IsAscii;
+using mozilla::Maybe;
 using mozilla::PodCopy;
+using mozilla::Utf8Unit;
 
 template <typename CharT>
 const CharT* js_strchr_limit(const CharT* s, char16_t c, const CharT* limit) {
   while (s < limit) {
     if (*s == c) {
       return s;
     }
     s++;
@@ -302,8 +314,56 @@ template size_t js::PutEscapedStringImpl
 
 template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
                                      const Latin1Char* chars, size_t length,
                                      uint32_t quote);
 
 template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
                                      const char16_t* chars, size_t length,
                                      uint32_t quote);
+
+size_t js::unicode::CountCodePoints(const Utf8Unit* begin,
+                                    const Utf8Unit* end) {
+  MOZ_ASSERT(begin <= end);
+
+  size_t count = 0;
+  const Utf8Unit* ptr = begin;
+  while (ptr < end) {
+    count++;
+
+    Utf8Unit lead = *ptr++;
+    if (IsAscii(lead)) {
+      continue;
+    }
+
+#ifdef DEBUG
+    Maybe<char32_t> cp =
+#endif
+        DecodeOneUtf8CodePoint(lead, &ptr, end);
+    MOZ_ASSERT(cp.isSome());
+  }
+  MOZ_ASSERT(ptr == end, "bad code unit count in line?");
+
+  return count;
+}
+
+size_t js::unicode::CountCodePoints(const char16_t* begin,
+                                    const char16_t* end) {
+  MOZ_ASSERT(begin <= end);
+
+  size_t count = 0;
+
+  const char16_t* ptr = begin;
+  while (ptr < end) {
+    count++;
+
+    if (!IsLeadSurrogate(*ptr++)) {
+      continue;
+    }
+
+    if (ptr < end && IsTrailSurrogate(*ptr)) {
+      ptr++;
+    }
+  }
+  MOZ_ASSERT(ptr == end, "should have consumed the full range");
+
+  return count;
+}
--- a/js/src/util/Text.h
+++ b/js/src/util/Text.h
@@ -6,16 +6,17 @@
 
 #ifndef util_Text_h
 #define util_Text_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TextUtils.h"
+#include "mozilla/Utf8.h"
 
 #include <ctype.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string>
 
 #include "jsutil.h"
@@ -203,11 +204,28 @@ inline bool FileEscapedString(FILE* fp, 
   return res;
 }
 
 JSString* EncodeURI(JSContext* cx, const char* chars, size_t length);
 
 // Return true if input string contains a given flag in a comma separated list.
 bool ContainsFlag(const char* str, const char* flag);
 
+namespace unicode {
+
+/** Compute the number of code points in the valid UTF-8 range [begin, end). */
+extern size_t CountCodePoints(const mozilla::Utf8Unit* begin,
+                              const mozilla::Utf8Unit* end);
+
+/**
+ * Count the number of code points in [begin, end).
+ *
+ * Unlike the UTF-8 case above, consistent with legacy ECMAScript practice,
+ * every sequence of 16-bit units is considered valid.  Lone surrogates are
+ * treated as if they represented a code point of the same value.
+ */
+extern size_t CountCodePoints(const char16_t* begin, const char16_t* end);
+
+}  // namespace unicode
+
 }  // namespace js
 
 #endif  // util_Text_h
--- a/js/src/vm/CompilationAndEvaluation.cpp
+++ b/js/src/vm/CompilationAndEvaluation.cpp
@@ -190,16 +190,25 @@ bool JS::CompileForNonSyntacticScope(JSC
                                      const ReadOnlyCompileOptions& optionsArg,
                                      SourceText<char16_t>& srcBuf,
                                      JS::MutableHandleScript script) {
   CompileOptions options(cx, optionsArg);
   options.setNonSyntacticScope(true);
   return CompileSourceBuffer(cx, options, srcBuf, script);
 }
 
+bool JS::CompileUtf8ForNonSyntacticScope(
+    JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* bytes,
+    size_t length, JS::MutableHandleScript script) {
+  CompileOptions options(cx, optionsArg);
+  options.setNonSyntacticScope(true);
+
+  return ::CompileUtf8(cx, options, bytes, length, script);
+}
+
 bool JS::CompileLatin1ForNonSyntacticScope(
     JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* bytes,
     size_t length, JS::MutableHandleScript script) {
   CompileOptions options(cx, optionsArg);
   options.setNonSyntacticScope(true);
 
   return ::CompileLatin1(cx, options, bytes, length, script);
 }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -2554,33 +2554,35 @@ class MOZ_STACK_CLASS FunctionValidator 
     }
     MOZ_CRASH("unexpected literal type");
   }
   MOZ_MUST_USE bool writeCall(ParseNode* pn, Op op) {
     if (!encoder().writeOp(op)) {
       return false;
     }
 
-    TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
-    return callSiteLineNums_.append(
-        anyChars.srcCoords.lineNum(pn->pn_pos.begin));
+    return appendCallSiteLineNumber(pn);
   }
   MOZ_MUST_USE bool writeCall(ParseNode* pn, MozOp op) {
     if (!encoder().writeOp(op)) {
       return false;
     }
 
-    TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
-    return callSiteLineNums_.append(
-        anyChars.srcCoords.lineNum(pn->pn_pos.begin));
+    return appendCallSiteLineNumber(pn);
   }
   MOZ_MUST_USE bool prepareCall(ParseNode* pn) {
-    TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
-    return callSiteLineNums_.append(
-        anyChars.srcCoords.lineNum(pn->pn_pos.begin));
+    return appendCallSiteLineNumber(pn);
+  }
+
+ private:
+  MOZ_MUST_USE bool appendCallSiteLineNumber(ParseNode* node) {
+    const TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
+
+    auto lineToken = anyChars.lineToken(node->pn_pos.begin);
+    return callSiteLineNums_.append(anyChars.lineNumber(lineToken));
   }
 };
 
 } /* anonymous namespace */
 
 /*****************************************************************************/
 // asm.js type-checking and code-generation algorithm
 
@@ -5833,17 +5835,17 @@ static bool ParseFunction(ModuleValidato
                           unsigned* line) {
   auto& tokenStream = m.tokenStream();
 
   tokenStream.consumeKnownToken(TokenKind::Function,
                                 TokenStreamShared::Operand);
 
   auto& anyChars = tokenStream.anyCharsAccess();
   uint32_t toStringStart = anyChars.currentToken().pos.begin;
-  *line = anyChars.srcCoords.lineNum(anyChars.currentToken().pos.end);
+  *line = anyChars.lineNumber(anyChars.lineToken(toStringStart));
 
   TokenKind tk;
   if (!tokenStream.getToken(&tk, TokenStreamShared::Operand)) {
     return false;
   }
   if (tk == TokenKind::Mul) {
     return m.failCurrentOffset("unexpected generator function");
   }
--- a/js/xpconnect/idl/mozIJSSubScriptLoader.idl
+++ b/js/xpconnect/idl/mozIJSSubScriptLoader.idl
@@ -28,20 +28,19 @@ interface mozIJSSubScriptLoader : nsISup
      */
     [implicit_jscontext]
     jsval loadSubScript(in AString url, [optional] in jsval obj, [optional] in AString charset);
 
     /**
      * This method should only be called from JS!
      * In JS, the signature looks like:
      * rv = loadSubScript (url, optionsObject)
-     * @param url the url of the sub-script, it MUST be either a file:,
-     *            resource:, blob:, or chrome: url, and MUST be local.
+     * @param url the url of the UTF-8-encoded sub-script, which MUST be either
+     *            a file:, resource:, blob:, or chrome: url, and MUST be local.
      * @param optionsObject an object with parameters. Valid parameters are:
-     *                      - charset: specifying the character encoding of the file (default: ASCII)
      *                      - target:  an object to evaluate onto (default: global object of the caller)
      *                      - ignoreCache: if set to true, will bypass the cache for reading the file.
      *                      - async: if set to true, the script will be loaded
      *                        asynchronously, and a Promise is returned which
      *                        resolves to its result when execution is complete.
      *                      - wantReturnValue: If true, the script will return
      *                        the value of the last statement that it evaluated.
      *                        This option disables most optimizations in the
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -842,30 +842,30 @@ nsresult mozJSComponentLoader::ObjectFor
     if (realFile) {
       AutoMemMap map;
       MOZ_TRY(map.init(aComponentFile));
 
       // Note: exceptions will get handled further down;
       // don't early return for them here.
       auto buf = map.get<char>();
       if (reuseGlobal) {
-        CompileLatin1ForNonSyntacticScope(cx, options, buf.get(), map.size(),
-                                          &script);
+        CompileUtf8ForNonSyntacticScope(cx, options, buf.get(), map.size(),
+                                        &script);
       } else {
-        CompileLatin1(cx, options, buf.get(), map.size(), &script);
+        CompileUtf8(cx, options, buf.get(), map.size(), &script);
       }
     } else {
       nsCString str;
       MOZ_TRY_VAR(str, ReadScript(aInfo));
 
       if (reuseGlobal) {
-        CompileLatin1ForNonSyntacticScope(cx, options, str.get(), str.Length(),
-                                          &script);
+        CompileUtf8ForNonSyntacticScope(cx, options, str.get(), str.Length(),
+                                        &script);
       } else {
-        CompileLatin1(cx, options, str.get(), str.Length(), &script);
+        CompileUtf8(cx, options, str.get(), str.Length(), &script);
       }
     }
     // Propagate the exception, if one exists. Also, don't leave the stale
     // exception on this context.
     if (!script && aPropagateExceptions && jsapi.HasException()) {
       if (!jsapi.StealException(aException)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -51,17 +51,17 @@ class MOZ_STACK_CLASS LoadSubScriptOptio
       : OptionsBase(cx, options),
         target(cx),
         charset(VoidString()),
         ignoreCache(false),
         async(false),
         wantReturnValue(false) {}
 
   virtual bool Parse() override {
-    return ParseObject("target", &target) && ParseString("charset", charset) &&
+    return ParseObject("target", &target) &&
            ParseBoolean("ignoreCache", &ignoreCache) &&
            ParseBoolean("async", &async) &&
            ParseBoolean("wantReturnValue", &wantReturnValue);
   }
 
   RootedObject target;
   nsString charset;
   bool ignoreCache;
@@ -552,16 +552,18 @@ mozJSSubScriptLoader::LoadSubScriptWithO
                                                MutableHandleValue retval) {
   if (!optionsVal.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
   LoadSubScriptOptions options(cx, &optionsVal.toObject());
   if (!options.Parse()) {
     return NS_ERROR_INVALID_ARG;
   }
+
+  options.charset.AssignLiteral("UTF-8");
   return DoLoadSubScriptWithOptions(url, options, cx, retval);
 }
 
 nsresult mozJSSubScriptLoader::DoLoadSubScriptWithOptions(
     const nsAString& url, LoadSubScriptOptions& options, JSContext* cx,
     MutableHandleValue retval) {
   nsresult rv = NS_OK;
   RootedObject targetObj(cx);
--- a/js/xpconnect/tests/chrome/test_chrometoSource.xul
+++ b/js/xpconnect/tests/chrome/test_chrometoSource.xul
@@ -47,17 +47,17 @@ isnot(src.indexOf("return"), -1, "subscr
 
 ns = {};
 Services.scriptloader.loadSubScript(resolvedBase + "utf8_subscript.js", ns, "UTF-8");
 src = ns.f.toSource();
 isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
 
 ns = {};
 Services.scriptloader.loadSubScriptWithOptions(resolvedBase + "utf8_subscript.js",
-                                               {target: ns, charset: "UTF-8", ignoreCache: true});
+                                               {target: ns, ignoreCache: true});
 src = ns.f.toSource();
 isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
 
 ns = {};
 let b = new Blob([
   "var Exported = 17;"
 ]);
 var blobUrl = URL.createObjectURL(b);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4902,18 +4902,19 @@ static int32_t gNoiseIndent = 0;
 // inline-size and percentage max-inline-size, are handled elsewhere.)
 inline static bool FormControlShrinksForPercentISize(nsIFrame* aFrame) {
   if (!aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
     // Quick test to reject most frames.
     return false;
   }
 
   LayoutFrameType fType = aFrame->Type();
-  if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress) {
-    // progress and meter do have this shrinking behavior
+  if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress ||
+      fType == LayoutFrameType::Range) {
+    // progress, meter and range do have this shrinking behavior
     // FIXME: Maybe these should be nsIFormControlFrame?
     return true;
   }
 
   if (!static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
     // Not a form control.  This includes fieldsets, which do not
     // shrink.
     return false;
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -736,17 +736,17 @@ LogicalSize nsRangeFrame::ComputeAutoSiz
     autoSize.ISize(wm) = NSToCoordRound(CROSS_AXIS_EM_SIZE * em);
     autoSize.BSize(wm) = NSToCoordRound(MAIN_AXIS_EM_SIZE * em);
   }
 
   return autoSize.ConvertTo(aWM, wm);
 }
 
 nscoord nsRangeFrame::GetMinISize(gfxContext* aRenderingContext) {
-  return nscoord(0);
+  return GetPrefISize(aRenderingContext);
 }
 
 nscoord nsRangeFrame::GetPrefISize(gfxContext* aRenderingContext) {
   bool isInline = IsInlineOriented();
   auto em = StyleFont()->mFont.size * nsLayoutUtils::FontSizeInflationFor(this);
   return NSToCoordRound(em *
                         (isInline ? MAIN_AXIS_EM_SIZE : CROSS_AXIS_EM_SIZE));
 }
--- a/layout/forms/test/mochitest.ini
+++ b/layout/forms/test/mochitest.ini
@@ -26,17 +26,17 @@ skip-if = toolkit == 'android'
 [test_bug534785.html]
 [test_bug542914.html]
 [test_bug549170.html]
 [test_bug562447.html]
 [test_bug563642.html]
 [test_bug564115.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug571352.html]
-skip-if = (os == 'mac' && os_version == '10.10') || toolkit == 'android' #TIMED_OUT # OS X 10.10 - bug 947690
+skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug572406.html]
 [test_bug572649.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug595310.html]
 [test_bug620936.html]
 [test_bug644542.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug672810.html]
--- a/layout/forms/test/test_bug571352.html
+++ b/layout/forms/test/test_bug571352.html
@@ -6,52 +6,52 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Test for Bug 571352</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=571352">Mozilla Bug 571352</a>
-<p id="display"></p>
+<p id="display"><select multiple style="width:300px/*to avoid any overlay scrollbar messing with our mouse clicks*/"><option>0<option>1<option>2<option>3<option>4<option>5</select></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 571352 **/
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function test() {
-  function createList() {
-    $('display').innerHTML = '<select multiple><option>0<option>1<option>2<option>3<option>4<option>5</select>';
+  function focusList() {
     $('display').firstChild.focus();
   }
   function option(index) {
     return $('display').firstChild.childNodes[index];
   }
   function remove(index) {
    var sel = $('display').firstChild;
    sel.removeChild(sel.childNodes[index]);
+   document.body.clientHeight;
   }
   function up() { synthesizeKey("KEY_ArrowUp"); }
   function shiftUp() { synthesizeKey("KEY_ArrowUp", {shiftKey:true}); }
   function down() { synthesizeKey("KEY_ArrowDown"); }
   function shiftDown() { synthesizeKey("KEY_ArrowDown", {shiftKey:true}); }
   function mouseEvent(index,event) {
     synthesizeMouse(option(index), 5, 5, event);
   }
 
   const click = {};
   const shiftClick = {shiftKey:true};
-  const CtrlClick = {CtrlKey:true};
-  createList();
+  focusList();
   mouseEvent(0,click)
   is(document.activeElement,$('display').firstChild,"<select> is focused");
+  ok(option(0).selected,"first option is selected");
   mouseEvent(2,shiftClick)
   remove(0);
   ok(option(0).selected && option(1).selected,"first two options are selected");
   mouseEvent(2,shiftClick)
   ok(option(0).selected && option(1).selected && option(2).selected,"first three options are selected");
   shiftUp();
   ok(option(0).selected && option(1).selected,"first two options are selected");
   remove(1);
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -696,23 +696,27 @@ nsresult ProxyAutoConfig::SetupJS() {
   // use nsIURI scheme methods
   bool isDataURI =
       nsDependentCSubstring(mPACURI, 0, 5).LowerCaseEqualsASCII("data:", 5);
 
   SetRunning(this);
 
   JS::Rooted<JSObject *> global(cx, mJSContext->Global());
 
-  JS::CompileOptions options(cx);
-  options.setFileAndLine(mPACURI.get(), 1);
+  auto CompilePACScript = [this](JSContext *cx,
+                                 JS::MutableHandle<JSScript *> script) {
+    JS::CompileOptions options(cx);
+    options.setFileAndLine(this->mPACURI.get(), 1);
+
+    return JS::CompileLatin1(cx, options, this->mPACScript.get(),
+                             this->mPACScript.Length(), script);
+  };
 
   JS::Rooted<JSScript *> script(cx);
-  if (!JS::CompileLatin1(cx, options, mPACScript.get(), mPACScript.Length(),
-                         &script) ||
-      !JS_ExecuteScript(cx, script)) {
+  if (!CompilePACScript(cx, &script) || !JS_ExecuteScript(cx, script)) {
     nsString alertMessage(
         NS_LITERAL_STRING("PAC file failed to install from "));
     if (isDataURI) {
       alertMessage += NS_LITERAL_STRING("data: URI");
     } else {
       alertMessage += NS_ConvertUTF8toUTF16(mPACURI);
     }
     PACLogToConsole(alertMessage);
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -989,18 +989,22 @@ class Artifacts(object):
         # Add the "-debug" suffix to the guessed artifact job name
         # if MOZ_DEBUG is enabled.
         if self._substs.get('MOZ_DEBUG'):
             target_suffix = '-debug'
         else:
             target_suffix = '-opt'
 
         if self._substs.get('MOZ_BUILD_APP', '') == 'mobile/android':
+            if self._substs['ANDROID_CPU_ARCH'] == 'x86_64':
+                return 'android-x86_64' + target_suffix
             if self._substs['ANDROID_CPU_ARCH'] == 'x86':
-                return 'android-x86-opt'
+                return 'android-x86' + target_suffix
+            if self._substs['ANDROID_CPU_ARCH'] == 'arm64-v8a':
+                return 'android-aarch64' + target_suffix
             return 'android-api-16' + target_suffix
 
         target_64bit = False
         if self._substs['target_cpu'] == 'x86_64':
             target_64bit = True
 
         if self._defines.get('XP_LINUX', False):
             return ('linux64' if target_64bit else 'linux') + target_suffix
--- a/servo/ports/geckolib/tests/build.rs
+++ b/servo/ports/geckolib/tests/build.rs
@@ -80,16 +80,17 @@ fn main() {
         let mut w = File::create(output).unwrap();
 
         w.write_all(b"pub use style::gecko::arc_types::*;\n")
             .unwrap();
 
         for line in r.lines() {
             let s = line
                 .unwrap()
+                .replace("#[no_mangle]", "")
                 .replace("pub extern \"C\" fn", "pub unsafe extern \"C\" fn");
             w.write_all(s.as_bytes()).unwrap();
             w.write_all(b"\n").unwrap();
         }
     }
 
     File::create(out_dir.join("bindings.rs"))
         .unwrap()
--- a/servo/ports/geckolib/tests/servo_function_signatures.rs
+++ b/servo/ports/geckolib/tests/servo_function_signatures.rs
@@ -11,16 +11,20 @@ use style::gecko_properties::*;
 include!(concat!(env!("OUT_DIR"), "/check_bindings.rs"));
 
 #[path = "../../../ports/geckolib/error_reporter.rs"]
 mod error_reporter;
 
 #[path = "../../../ports/geckolib/stylesheet_loader.rs"]
 mod stylesheet_loader;
 
-#[allow(non_snake_case, unused_unsafe, private_no_mangle_fns)]
+#[allow(non_snake_case, unused_unsafe)]
 mod glue {
-    // this module pretends to be glue.rs, with the safe functions swapped for unsafe ones. This is
-    // a hack to compensate for the fact that `fn` types cannot coerce to `unsafe fn` types. The
-    // imports are populated with the same things so the type assertion should be equivalent
+    // this module pretends to be glue.rs, with the safe functions swapped for
+    // unsafe ones. This is a hack to compensate for the fact that `fn` types
+    // cannot coerce to `unsafe fn` types. The imports are populated with the
+    // same things so the type assertion should be equivalent.
+    //
+    // We also rely on #[no_mangle] being stripped out so that it can link on
+    // Windows without linking to Gecko, see bug 1512271.
     use geckoservo::*;
     include!(concat!(env!("OUT_DIR"), "/glue.rs"));
 }
--- a/taskcluster/ci/toolchain/linux.yml
+++ b/taskcluster/ci/toolchain/linux.yml
@@ -488,28 +488,51 @@ linux64-rust-1.28:
         ]
         toolchain-artifact: public/build/rustc.tar.xz
 
 linux64-rust-1.30:
     description: "rust repack"
     treeherder:
         kind: build
         platform: toolchains/opt
+        symbol: TL(rust-1.30)
+        tier: 1
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        max-run-time: 7200
+        env:
+            UPLOAD_DIR: artifacts
+    run:
+        using: toolchain-script
+        script: repack_rust.py
+        arguments: [
+            '--channel', '1.30.0',
+            '--host', 'x86_64-unknown-linux-gnu',
+            '--target', 'x86_64-unknown-linux-gnu',
+            '--target', 'i686-unknown-linux-gnu',
+        ]
+        toolchain-artifact: public/build/rustc.tar.xz
+
+linux64-rust-1.31:
+    description: "rust repack"
+    treeherder:
+        kind: build
+        platform: toolchains/opt
         symbol: TL(rust)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', '1.30.0',
+            '--channel', '1.31.0',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'i686-unknown-linux-gnu',
         ]
         toolchain-alias: linux64-rust
         toolchain-artifact: public/build/rustc.tar.xz
 
 linux64-rust-nightly:
@@ -530,33 +553,33 @@ linux64-rust-nightly:
         arguments: [
             '--channel', 'nightly-2018-10-05',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'i686-unknown-linux-gnu',
         ]
         toolchain-artifact: public/build/rustc.tar.xz
 
-linux64-rust-macos-1.30:
+linux64-rust-macos-1.31:
     description: "rust repack with macos-cross support"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(rust-macos)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', '1.30.0',
+            '--channel', '1.31.0',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-apple-darwin',
         ]
         toolchain-alias: linux64-rust-macos
         toolchain-artifact: public/build/rustc.tar.xz
 
 linux64-rust-nightly-macos:
@@ -577,33 +600,33 @@ linux64-rust-nightly-macos:
         arguments: [
             '--channel', 'nightly-2018-10-05',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-apple-darwin',
         ]
         toolchain-artifact: public/build/rustc.tar.xz
 
-linux64-rust-android-1.30:
+linux64-rust-android-1.31:
     description: "rust repack with android-cross support"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(rust-android)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', '1.30.0',
+            '--channel', '1.31.0',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'armv7-linux-androideabi',
             '--target', 'aarch64-linux-android',
             '--target', 'i686-linux-android',
             '--target', 'x86_64-linux-android',
         ]
         toolchain-alias: linux64-rust-android
--- a/taskcluster/ci/toolchain/macosx.yml
+++ b/taskcluster/ci/toolchain/macosx.yml
@@ -172,9 +172,9 @@ macosx64-cbindgen:
         tooltool-downloads: internal
         resources:
             - 'taskcluster/scripts/misc/tooltool-download.sh'
         toolchain-artifact: public/build/cbindgen.tar.xz
     toolchains:
         - linux64-cctools-port
         - linux64-clang-7
         - linux64-llvm-dsymutil
-        - linux64-rust-macos-1.30
+        - linux64-rust-macos-1.31
--- a/taskcluster/ci/toolchain/windows.yml
+++ b/taskcluster/ci/toolchain/windows.yml
@@ -95,34 +95,34 @@ win64-rust-1.28:
         arguments: [
             '--channel', '1.28.0',
             '--host', 'x86_64-pc-windows-msvc',
             '--target', 'x86_64-pc-windows-msvc',
             '--target', 'i686-pc-windows-msvc',
         ]
         toolchain-artifact: public/build/rustc.tar.bz2
 
-win64-rust-1.30:
+win64-rust-1.31:
     description: "rust repack"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW64(rust)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: toolchain-build}
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', '1.30.0',
+            '--channel', '1.31.0',
             '--host', 'x86_64-pc-windows-msvc',
             '--target', 'x86_64-pc-windows-msvc',
             '--target', 'i686-pc-windows-msvc',
         ]
         toolchain-alias: win64-rust
         toolchain-artifact: public/build/rustc.tar.bz2
 
 win64-rust-nightly:
@@ -186,34 +186,34 @@ win64-node:
         docker-image: {in-tree: toolchain-build}
         max-run-time: 1800
     run:
         using: toolchain-script
         script: repack-node.sh
         arguments: ['win64']
         toolchain-artifact: public/build/node.tar.bz2
 
-mingw32-rust-1.30:
+mingw32-rust-1.31:
     description: "rust repack"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TMW(rust)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: toolchain-build}
         max-run-time: 7200
         env:
             UPLOAD_DIR: artifacts
     run:
         using: toolchain-script
         script: repack_rust.py
         arguments: [
-            '--channel', '1.30.0',
+            '--channel', '1.31.0',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'i686-unknown-linux-gnu',
             '--target', 'i686-pc-windows-gnu',
             '--target', 'x86_64-pc-windows-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
         ]
         toolchain-alias: mingw32-rust
         toolchain-artifact: public/build/rustc.tar.xz
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size-ref.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: type=range intrinsic size</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
+  <style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace;
+}
+
+.flex {
+  display: inline-flex;
+  width: 0;
+  border: 1px solid;
+  justify-items:start;
+}
+.flex2 {
+  display: inline-flex;
+  border: 1px solid;
+  justify-items:start;
+}
+.grid {
+  display: inline-grid;
+  grid: auto / 0;
+  border: 1px solid;
+  justify-items:start;
+}
+.grid2 {
+  display: inline-grid;
+  border: 1px solid;
+  justify-items:start;
+}
+.ib {
+  display: inline-block;
+  width: 0;
+  border: 1px solid;
+  justify-items:start;
+}
+
+input {
+   width: -moz-max-content;
+   width: max-content;
+   min-width: 0;
+}
+input.min {
+   min-width: -moz-min-content;
+   min-width: min-content;
+}
+input.mbp0 {
+  margin-left: 0;
+  margin-right: 0;
+  padding: 0;
+  border: 0;
+}
+  </style>
+</head>
+<body>
+
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" style="width:0"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<div class="flex"><input type="range" class="min"></div><br>
+<br>
+
+<div class="flex2"><input type="range"></div>
+<div class="flex2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<div class="flex2"><input type="range"></div>
+<br>
+
+<div class="grid"><input type="range" style="width:0"></div><br>
+<div class="grid"><input type="range" style="width:0"></div><br>
+<div class="grid" style="justify-items:start"><input type="range"></div><br>
+
+<div class="grid2"><input type="range"></div>
+<div class="grid2"><input type="range" style="min-width:0"></div>
+<div class="grid2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
+<div class="grid2" style="justify-items:start"><input type="range"></div>
+
+<br>
+
+<div class="ib"><input type="range"></div><br>
+<div class="ib"><input type="range"></div><br>
+
+<input type="range">
+<input type="range"
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/range-intrinsic-size.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Test: type=range intrinsic size</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
+  <link rel="match" href="range-intrinsic-size-ref.html">
+  <style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace;
+}
+
+.flex {
+  display: inline-flex;
+  width: 0;
+  border: 1px solid;
+}
+.flex2 {
+  display: inline-flex;
+  border: 1px solid;
+}
+.grid {
+  display: inline-grid;
+  grid: auto / 0;
+  border: 1px solid;
+}
+.grid2 {
+  display: inline-grid;
+  border: 1px solid;
+}
+.ib {
+  display: inline-block;
+  width: 0;
+  border: 1px solid;
+}
+input.mbp0 {
+  margin-left: 0;
+  margin-right: 0;
+  padding: 0;
+  border: 0;
+}
+  </style>
+</head>
+<body>
+
+<div class="flex"><input type="range"></div><br>
+<div class="flex"><input type="range" style="min-width:0"></div><br>
+<div class="flex" style="justify-items:start"><input type="range"></div><br>
+<div class="flex" style="-webkit-box-pack: start"><input type="range"></div><br>
+<div class="flex" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div><br>
+<br>
+
+<div class="flex2"><input type="range"></div>
+<div class="flex2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="flex2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="flex2"><input type="range" style="min-width:0"></div>
+<div class="flex2" style="justify-items:start"><input type="range"></div>
+<div class="flex2" style="-webkit-box-pack: start"><input type="range"></div>
+<div class="flex2" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div>
+<br>
+
+<div class="grid"><input type="range"></div><br>
+<div class="grid"><input type="range" style="min-width:0"></div><br>
+<div class="grid" style="justify-items:start"><input type="range"></div><br>
+
+<div class="grid2"><input type="range"></div>
+<div class="grid2"><input type="range" style="min-width:0"></div>
+<div class="grid2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="grid2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
+<div class="grid2" style="grid:auto/30px"><input type="range" class="mbp0"></div>
+<div class="grid2" style="justify-items:start"><input type="range"></div>
+
+<br>
+
+<div class="ib"><input type="range"></div><br>
+<div class="ib"><input type="range" style="min-width:0"></div><br>
+
+<input type="range" style="width:-moz-min-content; width:min-content;">
+<input type="range" style="width:-moz-max-content; width:max-content;">
+
+</body>
+</html>
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1011,24 +1011,25 @@
     "low": 32768,
     "high": 16777216,
     "n_buckets": 100,
     "bug_numbers": [1226196],
     "description": "Resident memory size (KB)"
   },
   "MEMORY_TOTAL": {
     "record_in_processes": ["main"],
-    "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
-    "bug_numbers": [1198209],
+    "alert_emails": ["memshrink-telemetry-alerts@mozilla.com", "erahm@mozilla.com"],
+    "bug_numbers": [1198209, 1511918],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 32768,
     "high": 16777216,
     "n_buckets": 100,
-    "description": "Total Memory Across All Processes (KB)"
+    "description": "Total Memory Across All Processes (KB)",
+    "releaseChannelCollection": "opt-out"
   },
   "MEMORY_DISTRIBUTION_AMONG_CONTENT": {
     "record_in_processes": ["main"],
     "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
     "bug_numbers": [1344174],
     "expires_in_version": "never",
     "kind": "linear",
     "keyed": true,
@@ -6523,23 +6524,24 @@
     "expires_in_version": "64",
     "kind": "categorical",
     "labels": ["bookmark", "pocket", "screenshots", "webcompat", "copyURL", "emailLink",
                "sendToDevice", "other", "addSearchEngine", "shareURL", "screenshotsMozillaOr"],
     "description": "Count how many times people use items from the main page action button"
   },
   "INPUT_EVENT_RESPONSE_MS": {
     "record_in_processes": ["main", "content"],
-    "alert_emails": ["perf-telemetry-alerts@mozilla.com"],
-    "bug_numbers": [1235908],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 10000,
-    "n_buckets": 50,
-    "description": "Time (ms) from the Input event being created to the end of it being handled"
+    "alert_emails": ["perf-telemetry-alerts@mozilla.com", "chutten@mozilla.com"],
+    "bug_numbers": [1235908, 1511919],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 50,
+    "description": "Time (ms) from the Input event being created to the end of it being handled",
+    "releaseChannelCollection": "opt-out"
   },
   "INPUT_EVENT_RESPONSE_COALESCED_MS": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["perf-telemetry-alerts@mozilla.com", "chutten@mozilla.com", "gfritzsche@mozilla.com"],
     "bug_numbers": [1357457, 1489524],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
--- a/toolkit/components/telemetry/app/TelemetryController.jsm
+++ b/toolkit/components/telemetry/app/TelemetryController.jsm
@@ -53,17 +53,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyModuleGetters(this, {
   ClientID: "resource://gre/modules/ClientID.jsm",
   CoveragePing: "resource://gre/modules/CoveragePing.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   TelemetryStorage: "resource://gre/modules/TelemetryStorage.jsm",
   TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
   TelemetryArchive: "resource://gre/modules/TelemetryArchive.jsm",
   TelemetrySession: "resource://gre/modules/TelemetrySession.jsm",
-  MemoryTelemetry: "resource://gre/modules/MemoryTelemetry.jsm",
   TelemetrySend: "resource://gre/modules/TelemetrySend.jsm",
   TelemetryReportingPolicy: "resource://gre/modules/TelemetryReportingPolicy.jsm",
   TelemetryModules: "resource://gre/modules/ModulesPing.jsm",
   TelemetryUntrustedModulesPing: "resource://gre/modules/UntrustedModulesPing.jsm",
   UpdatePing: "resource://gre/modules/UpdatePing.jsm",
   TelemetryHealthPing: "resource://gre/modules/HealthPing.jsm",
   TelemetryEventPing: "resource://gre/modules/EventPing.jsm",
   OS: "resource://gre/modules/osfile.jsm",
@@ -634,17 +633,17 @@ var Impl = {
       return Promise.resolve();
     }
 
     this._attachObservers();
 
     // Perform a lightweight, early initialization for the component, just registering
     // a few observers and initializing the session.
     TelemetrySession.earlyInit(this._testMode);
-    MemoryTelemetry.earlyInit(this._testMode);
+    Services.telemetry.earlyInit();
 
     // Annotate crash reports so that we get pings for startup crashes
     TelemetrySend.earlyInit();
 
     // For very short session durations, we may never load the client
     // id from disk.
     // We try to cache it in prefs to avoid this, even though this may
     // lead to some stale client ids.
@@ -684,17 +683,17 @@ var Impl = {
           this._log.trace("Not unified, but got canary client ID. Resetting.");
           this._clientID = await ClientID.resetClientID();
         }
 
         await TelemetrySend.setup(this._testMode);
 
         // Perform TelemetrySession delayed init.
         await TelemetrySession.delayedInit();
-        await MemoryTelemetry.delayedInit();
+        await Services.telemetry.delayedInit();
 
         if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.NewProfilePingEnabled, false) &&
             !TelemetrySession.newProfilePingSent) {
           // Kick off the scheduling of the new-profile ping.
           this.scheduleNewProfilePing();
         }
 
         // Purge the pings archive by removing outdated pings. We don't wait for
@@ -750,17 +749,25 @@ var Impl = {
     this._testMode = testing;
 
     // We call |enableTelemetryRecording| here to make sure that Telemetry.canRecord* flags
     // are in sync between chrome and content processes.
     if (!this.enableTelemetryRecording()) {
       this._log.trace("setupContentTelemetry - Content process recording disabled.");
       return;
     }
-    MemoryTelemetry.setupContent(testing);
+    Services.telemetry.earlyInit();
+
+    // FIXME: This is a terrible abuse of DeferredTask.
+    let delayedTask = new DeferredTask(() => {
+      Services.telemetry.delayedInit();
+    }, testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY,
+       testing ? 0 : undefined);
+
+    delayedTask.arm();
   },
 
   // Do proper shutdown waiting and cleanup.
   async _cleanupOnShutdown() {
     if (!this._initialized) {
       return;
     }
 
@@ -783,17 +790,17 @@ var Impl = {
 
       // Stop any ping sending.
       await TelemetrySend.shutdown();
 
       // Send latest data.
       await TelemetryHealthPing.shutdown();
 
       await TelemetrySession.shutdown();
-      await MemoryTelemetry.shutdown();
+      await Services.telemetry.shutdown();
 
       // First wait for clients processing shutdown.
       await this._shutdownBarrier.wait();
 
       // ... and wait for any outstanding async ping activity.
       await this._connectionsBarrier.wait();
 
       // Perform final shutdown operations.
--- a/toolkit/components/telemetry/core/Telemetry.cpp
+++ b/toolkit/components/telemetry/core/Telemetry.cpp
@@ -28,16 +28,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FStream.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/MemoryTelemetry.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/PoisonIOInterposer.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessedStack.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Unused.h"
@@ -1731,16 +1732,49 @@ TelemetryImpl::SetEventRecordingEnabled(
 }
 
 NS_IMETHODIMP
 TelemetryImpl::FlushBatchedChildTelemetry() {
   TelemetryIPCAccumulator::IPCTimerFired(nullptr, nullptr);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+TelemetryImpl::EarlyInit() {
+  Unused << MemoryTelemetry::Get();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelemetryImpl::DelayedInit() {
+  MemoryTelemetry::Get().DelayedInit();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelemetryImpl::Shutdown() {
+  MemoryTelemetry::Get().Shutdown();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelemetryImpl::GatherMemory(JSContext* aCx, Promise** aResult) {
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
+  if (rv.Failed()) {
+    return rv.StealNSResult();
+  }
+
+  MemoryTelemetry::Get().GatherReports(
+      [promise]() { promise->MaybeResolve(JS::UndefinedHandleValue); });
+
+  promise.forget(aResult);
+  return NS_OK;
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
 // EXTERNALLY VISIBLE FUNCTIONS in no name space
 // These are NOT listed in Telemetry.h
 
--- a/toolkit/components/telemetry/core/nsITelemetry.idl
+++ b/toolkit/components/telemetry/core/nsITelemetry.idl
@@ -598,9 +598,38 @@ interface nsITelemetry : nsISupports
    *
    * This is needed for supporting the current implementation of GeckoView
    * measurement persistence: all the measurements are stored in a single file and
    * they can't be cleared independently.
    *
    * Please note that this is only intended to be used by GeckoViewTelemetryController.
    */
   void clearProbes();
+
+  /**
+   * Does early, cheap initialization for native telemetry data providers.
+   * Currently, this includes only MemoryTelemetry.
+   */
+  void earlyInit();
+
+  /**
+   * Does late, expensive initialization for native telemetry data providers.
+   * Currently, this includes only MemoryTelemetry.
+   *
+   * This should only be called after startup has completed and the event loop
+   * is idle.
+   */
+  void delayedInit();
+
+  /**
+   * Shuts down native telemetry providers. Currently, this includes only
+   * MemoryTelemetry.
+   */
+  void shutdown();
+
+  /**
+   * Gathers telemetry data for memory usage and records it to the data store.
+   * Returns a promise which resolves when asynchronous data collection has
+   * completed and all data has been recorded.
+   */
+  [implicit_jscontext]
+  Promise gatherMemory();
 };
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -40,16 +40,17 @@ if CONFIG['ENABLE_TESTS']:
 
 TEST_DIRS += ['tests']
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 
 XPIDL_SOURCES += [
     'core/nsITelemetry.idl',
+    'other/GCTelemetry.idl',
 ]
 
 XPIDL_MODULE = 'telemetry'
 
 EXPORTS.mozilla += [
     '!TelemetryEventEnums.h',
     '!TelemetryHistogramEnums.h',
     '!TelemetryProcessEnums.h',
@@ -102,17 +103,16 @@ EXTRA_JS_MODULES += [
     'app/TelemetryController.jsm',
     'app/TelemetryEnvironment.jsm',
     'app/TelemetryReportingPolicy.jsm',
     'app/TelemetrySend.jsm',
     'app/TelemetryStorage.jsm',
     'app/TelemetryTimestamps.jsm',
     'app/TelemetryUtils.jsm',
     'other/GCTelemetry.jsm',
-    'other/MemoryTelemetry.jsm',
     'other/UITelemetry.jsm',
     'pings/CoveragePing.jsm',
     'pings/EventPing.jsm',
     'pings/HealthPing.jsm',
     'pings/ModulesPing.jsm',
     'pings/TelemetrySession.jsm',
     'pings/UntrustedModulesPing.jsm',
     'pings/UpdatePing.jsm',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/other/GCTelemetry.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(6ab1c3c1-31cf-4a32-8484-97b5ef0627af)]
+interface mozIGCTelemetry : nsISupports {
+  void init();
+
+  void shutdown();
+};
+
+[scriptable, uuid(93b2a0ca-6306-41c1-b296-c57cad5175c7)]
+interface mozIGCTelemetryJSM : nsISupports {
+  readonly attribute mozIGCTelemetry GCTelemetry;
+};
deleted file mode 100644
--- a/toolkit/components/telemetry/other/MemoryTelemetry.jsm
+++ /dev/null
@@ -1,540 +0,0 @@
-/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
-/* 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/. */
-
-"use strict";
-
-ChromeUtils.import("resource://gre/modules/Log.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
-ChromeUtils.import("resource://gre/modules/DeferredTask.jsm", this);
-ChromeUtils.import("resource://gre/modules/Timer.jsm");
-ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  GCTelemetry: "resource://gre/modules/GCTelemetry.jsm",
-});
-
-const Utils = TelemetryUtils;
-
-const LOGGER_NAME = "Toolkit.Telemetry";
-const LOGGER_PREFIX = "MemoryTelemetry" + (Utils.isContentProcess ? "#content::" : "::");
-
-const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
-const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
-
-// Do not gather data more than once a minute (ms)
-const TELEMETRY_INTERVAL = 60 * 1000;
-// Delay before intializing telemetry (ms)
-const TELEMETRY_DELAY = Services.prefs.getIntPref("toolkit.telemetry.initDelay", 60) * 1000;
-// Delay before initializing telemetry if we're testing (ms)
-const TELEMETRY_TEST_DELAY = 1;
-
-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;
-
-var gLastMemoryPoll = null;
-
-XPCOMUtils.defineLazyServiceGetters(this, {
-  Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
-});
-
-var EXPORTED_SYMBOLS = ["MemoryTelemetry"];
-
-var MemoryTelemetry = Object.freeze({
-  /**
-   * Pull values from about:memory into corresponding histograms
-   */
-  gatherMemory() {
-    return Impl.gatherMemory();
-  },
-
-  /**
-   * Triggers shutdown of the module.
-   */
-  shutdown() {
-    return Impl.shutdown();
-  },
-
-  /**
-   * Sets up components used in the content process.
-   */
-  setupContent(testing = false) {
-    return Impl.setupContent(testing);
-  },
-
-  /**
-   * Lightweight init function, called as soon as Firefox starts.
-   */
-  earlyInit(aTesting = false) {
-    return Impl.earlyInit(aTesting);
-  },
-
-  /**
-   * Does the "heavy" Telemetry initialization later on, so we
-   * don't impact startup performance.
-   * @return {Promise} Resolved when the initialization completes.
-   */
-  delayedInit() {
-    return Impl.delayedInit();
-  },
-});
-
-var Impl = {
-  _initialized: false,
-  _logger: null,
-  _prevValues: {},
-  // A task performing delayed initialization of the chrome process
-  _delayedInitTask: null,
-  // Need a timeout in case children are tardy in giving back their memory reports.
-  _totalMemoryTimeout: undefined,
-  _testing: false,
-  // An accumulator of total memory across all processes. Only valid once the final child reports.
-  _totalMemory: null,
-  // A Set of outstanding USS report ids
-  _childrenToHearFrom: null,
-  // monotonically-increasing id for USS reports
-  _nextTotalMemoryId: 1,
-  _USSFromChildProcesses: null,
-  // Keep track of the active observers
-  _observedTopics: new Set(),
-
-  addObserver(aTopic) {
-    Services.obs.addObserver(this, aTopic);
-    this._observedTopics.add(aTopic);
-  },
-
-  removeObserver(aTopic) {
-    Services.obs.removeObserver(this, aTopic);
-    this._observedTopics.delete(aTopic);
-  },
-
-  get _log() {
-    if (!this._logger) {
-      this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
-    }
-    return this._logger;
-  },
-
-  /**
-   * Pull values from about:memory into corresponding histograms
-   */
-  gatherMemory: function gatherMemory() {
-    let mgr;
-    try {
-      mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
-            getService(Ci.nsIMemoryReporterManager);
-    } catch (e) {
-      // OK to skip memory reporters in xpcshell
-      return;
-    }
-
-    let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS");
-    let startTime = new Date();
-
-    // Get memory measurements from distinguished amount attributes.  We used
-    // to measure "explicit" too, but it could cause hangs, and the data was
-    // always really noisy anyway.  See bug 859657.
-    //
-    // test_TelemetrySession.js relies on some of these histograms being
-    // here.  If you remove any of the following histograms from here, you'll
-    // have to modify test_TelemetrySession.js:
-    //
-    //   * MEMORY_JS_GC_HEAP, and
-    //   * MEMORY_JS_COMPARTMENTS_SYSTEM.
-    //
-    // The distinguished amount attribute names don't match the telemetry id
-    // names in some cases due to a combination of (a) historical reasons, and
-    // (b) the fact that we can't change telemetry id names without breaking
-    // data continuity.
-    //
-    let boundHandleMemoryReport = this.handleMemoryReport.bind(this);
-    let h = (id, units, amountName) => {
-      try {
-        // If mgr[amountName] throws an exception, just move on -- some amounts
-        // aren't available on all platforms.  But if the attribute simply
-        // isn't present, that indicates the distinguished amounts have changed
-        // and this file hasn't been updated appropriately.
-        let amount = mgr[amountName];
-        if (amount === undefined) {
-          this._log.error("gatherMemory - telemetry accessed an unknown distinguished amount");
-        }
-        boundHandleMemoryReport(id, units, amount);
-      } catch (e) {
-      }
-    };
-    let b = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_BYTES, n);
-    let c = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT, n);
-    let cc = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE, n);
-    let p = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_PERCENTAGE, n);
-
-    // GHOST_WINDOWS is opt-out as of Firefox 55
-    c("GHOST_WINDOWS", "ghostWindows");
-
-    if (!Telemetry.canRecordExtended) {
-      return;
-    }
-
-    b("MEMORY_VSIZE", "vsize");
-    if (!Services.appinfo.is64Bit || AppConstants.platform !== "win") {
-      b("MEMORY_VSIZE_MAX_CONTIGUOUS", "vsizeMaxContiguous");
-    }
-    b("MEMORY_RESIDENT_FAST", "residentFast");
-    b("MEMORY_UNIQUE", "residentUnique");
-    p("MEMORY_HEAP_OVERHEAD_FRACTION", "heapOverheadFraction");
-    b("MEMORY_JS_GC_HEAP", "JSMainRuntimeGCHeap");
-    c("MEMORY_JS_COMPARTMENTS_SYSTEM", "JSMainRuntimeRealmsSystem");
-    c("MEMORY_JS_COMPARTMENTS_USER", "JSMainRuntimeRealmsUser");
-    b("MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED", "imagesContentUsedUncompressed");
-    b("MEMORY_STORAGE_SQLITE", "storageSQLite");
-    cc("LOW_MEMORY_EVENTS_VIRTUAL", "lowMemoryEventsVirtual");
-    cc("LOW_MEMORY_EVENTS_COMMIT_SPACE", "lowMemoryEventsCommitSpace");
-    cc("LOW_MEMORY_EVENTS_PHYSICAL", "lowMemoryEventsPhysical");
-    cc("PAGE_FAULTS_HARD", "pageFaultsHard");
-
-    try {
-      mgr.getHeapAllocatedAsync(heapAllocated => {
-        boundHandleMemoryReport("MEMORY_HEAP_ALLOCATED",
-                                Ci.nsIMemoryReporter.UNITS_BYTES,
-                                heapAllocated);
-      });
-    } catch (e) {
-    }
-
-    if (!Utils.isContentProcess && !this._totalMemoryTimeout) {
-      // Only the chrome process should gather total memory
-      // total = parent RSS + sum(child USS)
-      this._totalMemory = mgr.residentFast;
-      if (Services.ppmm.childCount > 1) {
-        // Do not report If we time out waiting for the children to call
-        this._totalMemoryTimeout = setTimeout(
-          () => {
-            this._totalMemoryTimeout = undefined;
-            this._childrenToHearFrom.clear();
-          },
-          TOTAL_MEMORY_COLLECTOR_TIMEOUT);
-        this._USSFromChildProcesses = [];
-        this._childrenToHearFrom = new Set();
-        for (let i = 1; i < Services.ppmm.childCount; i++) {
-          let child = Services.ppmm.getChildAt(i);
-          try {
-            child.sendAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_USS, {id: this._nextTotalMemoryId});
-            this._childrenToHearFrom.add(this._nextTotalMemoryId);
-            this._nextTotalMemoryId++;
-          } catch (ex) {
-            // If a content process has just crashed, then attempting to send it
-            // an async message will throw an exception.
-            Cu.reportError(ex);
-          }
-        }
-      } else {
-        boundHandleMemoryReport(
-          "MEMORY_TOTAL",
-          Ci.nsIMemoryReporter.UNITS_BYTES,
-          this._totalMemory);
-      }
-    }
-
-    histogram.add(new Date() - startTime);
-  },
-
-  handleMemoryReport(id, units, amount, key) {
-    let val;
-    if (units == Ci.nsIMemoryReporter.UNITS_BYTES) {
-      val = Math.floor(amount / 1024);
-    } else if (units == Ci.nsIMemoryReporter.UNITS_PERCENTAGE) {
-      // UNITS_PERCENTAGE amounts are 100x greater than their raw value.
-      val = Math.floor(amount / 100);
-    } else if (units == Ci.nsIMemoryReporter.UNITS_COUNT) {
-      val = amount;
-    } else if (units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) {
-      // If the reporter gives us a cumulative count, we'll report the
-      // difference in its value between now and our previous ping.
-
-      if (!(id in this._prevValues)) {
-        // If this is the first time we're reading this reporter, store its
-        // current value but don't report it in the telemetry ping, so we
-        // ignore the effect startup had on the reporter.
-        this._prevValues[id] = amount;
-        return;
-      }
-
-      val = amount - this._prevValues[id];
-      this._prevValues[id] = amount;
-    } else {
-      this._log.error("handleMemoryReport - Can't handle memory reporter with units " + units);
-      return;
-    }
-
-    if (key) {
-      Telemetry.getKeyedHistogramById(id).add(key, val);
-      return;
-    }
-
-    Telemetry.getHistogramById(id).add(val);
-  },
-
-  attachObservers: function attachObservers() {
-    if (!this._initialized)
-      return;
-    if (Telemetry.canRecordExtended) {
-      this.addObserver(TOPIC_CYCLE_COLLECTOR_BEGIN);
-    }
-  },
-
-
-  /**
-   * Lightweight init function, called as soon as Firefox starts.
-   */
-  earlyInit(testing) {
-    this._log.trace("earlyInit");
-
-    this._initStarted = true;
-    this._testing = testing;
-
-    if (this._initialized && !testing) {
-      this._log.error("earlyInit - already initialized");
-      return;
-    }
-
-    if (!Telemetry.canRecordBase && !testing) {
-      this._log.config("earlyInit - Telemetry recording is disabled, skipping Chrome process setup.");
-      return;
-    }
-
-    Services.ppmm.addMessageListener(MESSAGE_TELEMETRY_USS, this);
-  },
-
-  /**
-   * Does the "heavy" Telemetry initialization later on, so we
-   * don't impact startup performance.
-   * @return {Promise} Resolved when the initialization completes.
-   */
-  delayedInit() {
-    this._log.trace("delayedInit");
-
-    this._delayedInitTask = (async () => {
-      try {
-        this._initialized = true;
-
-        this.attachObservers();
-        this.gatherMemory();
-
-        if (Telemetry.canRecordExtended) {
-          GCTelemetry.init();
-        }
-
-        this._delayedInitTask = null;
-      } catch (e) {
-        this._delayedInitTask = null;
-        throw e;
-      }
-    })();
-
-    return this._delayedInitTask;
-  },
-
-  /**
-   * Initializes telemetry for a content process.
-   */
-  setupContent: function setupContent(testing) {
-    this._log.trace("setupContent");
-    this._testing = testing;
-
-    if (!Telemetry.canRecordBase) {
-      this._log.trace("setupContent - base recording is disabled, not initializing");
-      return;
-    }
-
-    this.addObserver("content-child-shutdown");
-    Services.cpmm.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this);
-
-    let delayedTask = new DeferredTask(() => {
-      this._initialized = true;
-
-      this.attachObservers();
-      this.gatherMemory();
-
-      if (Telemetry.canRecordExtended) {
-        GCTelemetry.init();
-      }
-    }, testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY,
-    testing ? 0 : undefined);
-
-    delayedTask.arm();
-  },
-
-  getOpenTabsCount: function getOpenTabsCount() {
-    let tabCount = 0;
-
-    for (let win of Services.wm.getEnumerator("navigator:browser")) {
-      tabCount += win.gBrowser.tabs.length;
-    }
-
-    return tabCount;
-  },
-
-  receiveMessage: function receiveMessage(message) {
-    this._log.trace("receiveMessage - Message name " + message.name);
-    switch (message.name) {
-    case MESSAGE_TELEMETRY_USS:
-    {
-      // In parent process, receive the USS report from the child
-      if (this._totalMemoryTimeout && this._childrenToHearFrom.delete(message.data.id)) {
-        let uss = message.data.bytes;
-        this._totalMemory += uss;
-        this._USSFromChildProcesses.push(uss);
-        if (this._childrenToHearFrom.size == 0) {
-          clearTimeout(this._totalMemoryTimeout);
-          this._totalMemoryTimeout = undefined;
-          this.handleMemoryReport(
-            "MEMORY_TOTAL",
-            Ci.nsIMemoryReporter.UNITS_BYTES,
-            this._totalMemory);
-
-          let length = this._USSFromChildProcesses.length;
-          if (length > 1) {
-            // Mean of the USS of all the content processes.
-            let mean = this._USSFromChildProcesses.reduce((a, b) => a + b, 0) / length;
-            // Absolute error of USS for each content process, normalized by the mean (*100 to get it in percentage).
-            // 20% means for a content process that it is using 20% more or 20% less than the mean.
-            let diffs = this._USSFromChildProcesses.map(value => Math.floor(Math.abs(value - mean) * 100 / mean));
-            let tabsCount = this.getOpenTabsCount();
-            let key;
-            if (tabsCount < 11) {
-              key = "0 - 10 tabs";
-            } else if (tabsCount < 501) {
-              key = "11 - 500 tabs";
-            } else {
-              key = "more tabs";
-            }
-
-            diffs.forEach(value => {
-              this.handleMemoryReport(
-              "MEMORY_DISTRIBUTION_AMONG_CONTENT",
-              Ci.nsIMemoryReporter.UNITS_COUNT,
-              value,
-              key);
-            });
-
-            // This notification is for testing only.
-            Services.obs.notifyObservers(null, "gather-memory-telemetry-finished");
-          }
-          this._USSFromChildProcesses = undefined;
-        }
-      } else {
-        this._log.trace("Child USS report was missed");
-      }
-      break;
-    }
-    case MESSAGE_TELEMETRY_GET_CHILD_USS:
-    {
-      // In child process, send the requested USS report
-      this.sendContentProcessUSS(message.data.id);
-      break;
-    }
-    default:
-      throw new Error("Telemetry.receiveMessage: bad message name");
-    }
-  },
-
-  sendContentProcessUSS: function sendContentProcessUSS(aMessageId) {
-    this._log.trace("sendContentProcessUSS");
-
-    let mgr;
-    try {
-      mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
-            getService(Ci.nsIMemoryReporterManager);
-    } catch (e) {
-      // OK to skip memory reporters in xpcshell
-      return;
-    }
-
-    Services.cpmm.sendAsyncMessage(
-      MESSAGE_TELEMETRY_USS,
-      {bytes: mgr.residentUnique, id: aMessageId}
-    );
-  },
-
-  /**
-   * Do some shutdown work that is common to all process types.
-   */
-  uninstall() {
-    for (let topic of this._observedTopics) {
-      try {
-        // Tests may flip Telemetry.canRecordExtended on and off. It can be the case
-        // that the observer TOPIC_CYCLE_COLLECTOR_BEGIN was not added.
-        this.removeObserver(topic);
-      } catch (e) {
-        this._log.warn("uninstall - Failed to remove " + topic, e);
-      }
-    }
-
-    GCTelemetry.shutdown();
-  },
-
-  observe(aSubject, aTopic, aData) {
-    // Prevent the cycle collector begin topic from cluttering the log.
-    if (aTopic != TOPIC_CYCLE_COLLECTOR_BEGIN) {
-      this._log.trace("observe - " + aTopic + " notified.");
-    }
-
-    switch (aTopic) {
-    case "content-child-shutdown":
-      // content-child-shutdown is only registered for content processes.
-      this.uninstall();
-      Telemetry.flushBatchedChildTelemetry();
-      break;
-    case TOPIC_CYCLE_COLLECTOR_BEGIN:
-      let now = new Date();
-      if (!gLastMemoryPoll
-          || (TELEMETRY_INTERVAL <= now - gLastMemoryPoll)) {
-        gLastMemoryPoll = now;
-
-        this._log.trace("Dispatching idle gatherMemory task");
-        Services.tm.idleDispatchToMainThread(() => {
-          this._log.trace("Running idle gatherMemory task");
-          this.gatherMemory();
-          return true;
-        });
-      }
-      break;
-    }
-    return undefined;
-  },
-
-  shutdown() {
-    this._log.trace("shutdown");
-
-    let cleanup = () => {
-      this.uninstall();
-
-      this._initStarted = false;
-      this._initialized = false;
-    };
-
-    // We can be in one the following states here:
-    // 1) delayedInit was never called
-    // or it was called and
-    //   2) _delayedInitTask is running now.
-    //   3) _delayedInitTask finished running already.
-
-    // This handles 1).
-    if (!this._initStarted) {
-      return Promise.resolve();
-    }
-
-    // This handles 3).
-    if (!this._delayedInitTask) {
-      // We already ran the delayed initialization.
-      return cleanup();
-    }
-
-    // This handles 2).
-    return this._delayedInitTask.then(cleanup);
-  },
-};
--- a/toolkit/components/telemetry/pings/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/pings/TelemetrySession.jsm
@@ -11,17 +11,16 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   TelemetryController: "resource://gre/modules/TelemetryController.jsm",
   TelemetryStorage: "resource://gre/modules/TelemetryStorage.jsm",
-  MemoryTelemetry: "resource://gre/modules/MemoryTelemetry.jsm",
   UITelemetry: "resource://gre/modules/UITelemetry.jsm",
   GCTelemetry: "resource://gre/modules/GCTelemetry.jsm",
   TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
   TelemetryReportingPolicy: "resource://gre/modules/TelemetryReportingPolicy.jsm",
 });
 
 const Utils = TelemetryUtils;
 
@@ -1043,20 +1042,20 @@ var Impl = {
     }
 
     return payload;
   },
 
   /**
    * Send data to the server. Record success/send-time in histograms
    */
-  send: function send(reason) {
+  send: async function send(reason) {
     this._log.trace("send - Reason " + reason);
     // populate histograms one last time
-    MemoryTelemetry.gatherMemory();
+    await Services.telemetry.gatherMemory();
 
     const isSubsession = !this._isClassicReason(reason);
     let payload = this.getSessionPayload(reason, isSubsession);
     let options = {
       addClientId: true,
       addEnvironment: true,
     };
     return TelemetryController.submitExternalPing(getPingType(payload), payload, options);
@@ -1133,17 +1132,17 @@ var Impl = {
         this._initialized = true;
 
         await this._loadSessionData();
         // Update the session data to keep track of new subsessions created before
         // the initialization.
         await TelemetryStorage.saveSessionData(this._getSessionDataObject());
 
         this.addObserver("idle-daily");
-        MemoryTelemetry.gatherMemory();
+        await Services.telemetry.gatherMemory();
 
         Telemetry.asyncFetchTelemetryData(function() {});
 
         if (IS_UNIFIED_TELEMETRY) {
           // Check for a previously written aborted session ping.
           await TelemetryController.checkAbortedSessionPing();
 
           // Write the first aborted-session ping as early as possible. Just do that
@@ -1279,17 +1278,17 @@ var Impl = {
   getPayload: function getPayload(reason, clearSubsession) {
     this._log.trace("getPayload - clearSubsession: " + clearSubsession);
     reason = reason || REASON_GATHER_PAYLOAD;
     // This function returns the current Telemetry payload to the caller.
     // We only gather startup info once.
     if (Object.keys(this._slowSQLStartup).length == 0) {
       this._slowSQLStartup = Telemetry.slowSQL;
     }
-    MemoryTelemetry.gatherMemory();
+    Services.telemetry.gatherMemory();
     return this.getSessionPayload(reason, clearSubsession);
   },
 
   gatherStartup: function gatherStartup() {
     this._log.trace("gatherStartup");
     let counters = processInfo.getCounters();
     if (counters) {
       [this._startupIO.startupSessionRestoreReadBytes,
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -361,16 +361,17 @@ function checkPayload(payload, reason, s
   // memory reporters.  But we can at least check that the data is there.
   //
   // It's important to check for the presence of reporters with a mix of units,
   // because MemoryTelemetry has separate logic for each one.  But we can't
   // currently check UNITS_COUNT_CUMULATIVE or UNITS_PERCENTAGE because
   // Telemetry doesn't touch a memory reporter with these units that's
   // available on all platforms.
 
+  Assert.ok("MEMORY_TOTAL" in payload.histograms); // UNITS_BYTES
   Assert.ok("MEMORY_JS_GC_HEAP" in payload.histograms); // UNITS_BYTES
   Assert.ok("MEMORY_JS_COMPARTMENTS_SYSTEM" in payload.histograms); // UNITS_COUNT
 
   Assert.ok(("mainThread" in payload.slowSQL) &&
                 ("otherThreads" in payload.slowSQL));
 
   Assert.ok(("IceCandidatesStats" in payload.webrtc) &&
                 ("webrtc" in payload.webrtc.IceCandidatesStats));
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -3015,17 +3015,16 @@ WITH THE USE OR PERFORMANCE OF THIS SOFT
     <hr>
 
     <h1><a id="chromium"></a>Chromium License</h1>
 
     <p>This license applies to parts of the code in:</p>
     <ul>
 #ifndef RELEASE_OR_BETA
         <li><code>browser/extensions/formautofill/content/heuristicsRegexp.js</code></li>
-        <li><code>browser/extensions/formautofill/content/nameReferences.js</code></li>
         <li><code>browser/extensions/formautofill/FormAutofillHeuristics.jsm</code></li>
         <li><code>browser/extensions/formautofill/FormAutofillNameUtils.jsm</code></li>
 #endif
         <li><code>editor/libeditor/EditorEventListener.cpp</code></li>
         <li><code>mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StrictModeContext.java</code></li>
         <li><code>security/sandbox/</code></li>
         <li><code>widget/cocoa/GfxInfo.mm</code></li>
     </ul>
new file mode 100644
--- /dev/null
+++ b/xpcom/base/MemoryTelemetry.cpp
@@ -0,0 +1,525 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MemoryTelemetry.h"
+#include "nsMemoryReporterManager.h"
+
+#include "GCTelemetry.h"
+#include "mozJSComponentLoader.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/Services.h"
+#include "mozilla/SimpleEnumerator.h"
+#include "mozilla/SystemGroup.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsContentUtils.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIMemoryReporter.h"
+#include "nsIWindowMediator.h"
+#include "nsNetCID.h"
+#include "nsObserverService.h"
+#include "nsReadableUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "xpcpublic.h"
+
+#include <cstdlib>
+
+using namespace mozilla;
+
+using mozilla::dom::AutoJSAPI;
+using mozilla::dom::ContentParent;
+
+// Do not gather data more than once a minute (ms)
+static constexpr uint32_t kTelemetryInterval = 60 * 1000;
+
+static constexpr const char* kTopicCycleCollectorBegin =
+    "cycle-collector-begin";
+
+// How long to wait in millis for all the child memory reports to come in
+static constexpr uint32_t kTotalMemoryCollectorTimeout = 200;
+
+static Result<nsCOMPtr<mozIGCTelemetry>, nsresult> GetGCTelemetry() {
+  AutoJSAPI jsapi;
+  MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+  JSContext* cx = jsapi.cx();
+
+  JS::RootedObject global(cx);
+  JS::RootedObject exports(cx);
+  MOZ_TRY(mozJSComponentLoader::Get()->Import(
+      cx, NS_LITERAL_CSTRING("resource://gre/modules/GCTelemetry.jsm"), &global,
+      &exports));
+
+  nsCOMPtr<mozIGCTelemetryJSM> jsm;
+  MOZ_TRY(nsContentUtils::XPConnect()->WrapJS(
+      cx, exports, NS_GET_IID(mozIGCTelemetryJSM), getter_AddRefs(jsm)));
+
+  nsCOMPtr<mozIGCTelemetry> gcTelemetry;
+  MOZ_TRY(jsm->GetGCTelemetry(getter_AddRefs(gcTelemetry)));
+
+  return std::move(gcTelemetry);
+}
+
+namespace {
+
+enum class PrevValue : uint32_t {
+#ifdef XP_WIN
+  LOW_MEMORY_EVENTS_VIRTUAL,
+  LOW_MEMORY_EVENTS_COMMIT_SPACE,
+  LOW_MEMORY_EVENTS_PHYSICAL,
+#endif
+#if defined(XP_LINUX) && !defined(ANDROID)
+  PAGE_FAULTS_HARD,
+#endif
+  SIZE_,
+};
+
+}  // anonymous namespace
+
+constexpr uint32_t kUninitialized = ~0;
+
+static uint32_t gPrevValues[uint32_t(PrevValue::SIZE_)];
+
+static uint32_t PrevValueIndex(Telemetry::HistogramID aId) {
+  switch (aId) {
+#ifdef XP_WIN
+    case Telemetry::LOW_MEMORY_EVENTS_VIRTUAL:
+      return uint32_t(PrevValue::LOW_MEMORY_EVENTS_VIRTUAL);
+    case Telemetry::LOW_MEMORY_EVENTS_COMMIT_SPACE:
+      return uint32_t(PrevValue::LOW_MEMORY_EVENTS_COMMIT_SPACE);
+    case Telemetry::LOW_MEMORY_EVENTS_PHYSICAL:
+      return uint32_t(PrevValue::LOW_MEMORY_EVENTS_PHYSICAL);
+#endif
+#if defined(XP_LINUX) && !defined(ANDROID)
+    case Telemetry::PAGE_FAULTS_HARD:
+      return uint32_t(PrevValue::PAGE_FAULTS_HARD);
+#endif
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected histogram ID");
+      return 0;
+  }
+}
+
+NS_IMPL_ISUPPORTS(MemoryTelemetry, nsIObserver, nsISupportsWeakReference)
+
+MemoryTelemetry::MemoryTelemetry()
+    : mThreadPool(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID)) {}
+
+void MemoryTelemetry::Init() {
+  for (auto& val : gPrevValues) {
+    val = kUninitialized;
+  }
+
+  if (XRE_IsContentProcess()) {
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    MOZ_RELEASE_ASSERT(obs);
+
+    obs->AddObserver(this, "content-child-shutdown", true);
+  }
+}
+
+/* static */ MemoryTelemetry& MemoryTelemetry::Get() {
+  static RefPtr<MemoryTelemetry> sInstance;
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sInstance) {
+    sInstance = new MemoryTelemetry();
+    sInstance->Init();
+    ClearOnShutdown(&sInstance);
+  }
+  return *sInstance;
+}
+
+nsresult MemoryTelemetry::DelayedInit() {
+  if (Telemetry::CanRecordExtended()) {
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    MOZ_RELEASE_ASSERT(obs);
+
+    obs->AddObserver(this, kTopicCycleCollectorBegin, true);
+  }
+
+  GatherReports();
+
+  if (Telemetry::CanRecordExtended()) {
+    nsCOMPtr<mozIGCTelemetry> gcTelemetry;
+    MOZ_TRY_VAR(gcTelemetry, GetGCTelemetry());
+
+    MOZ_TRY(gcTelemetry->Init());
+  }
+
+  return NS_OK;
+}
+
+nsresult MemoryTelemetry::Shutdown() {
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  MOZ_RELEASE_ASSERT(obs);
+
+  obs->RemoveObserver(this, kTopicCycleCollectorBegin);
+
+  if (Telemetry::CanRecordExtended()) {
+    nsCOMPtr<mozIGCTelemetry> gcTelemetry;
+    MOZ_TRY_VAR(gcTelemetry, GetGCTelemetry());
+
+    MOZ_TRY(gcTelemetry->Shutdown());
+  }
+
+  return NS_OK;
+}
+
+static inline void HandleMemoryReport(Telemetry::HistogramID aId,
+                                      int32_t aUnits, uint64_t aAmount,
+                                      const nsCString& aKey = VoidCString()) {
+  uint32_t val;
+  switch (aUnits) {
+    case nsIMemoryReporter::UNITS_BYTES:
+      val = uint32_t(aAmount / 1024);
+      break;
+
+    case nsIMemoryReporter::UNITS_PERCENTAGE:
+      // UNITS_PERCENTAGE amounts are 100x greater than their raw value.
+      val = uint32_t(aAmount / 100);
+      break;
+
+    case nsIMemoryReporter::UNITS_COUNT:
+      val = uint32_t(aAmount);
+      break;
+
+    case nsIMemoryReporter::UNITS_COUNT_CUMULATIVE: {
+      // If the reporter gives us a cumulative count, we'll report the
+      // difference in its value between now and our previous ping.
+
+      uint32_t idx = PrevValueIndex(aId);
+      uint32_t prev = gPrevValues[idx];
+      gPrevValues[idx] = aAmount;
+
+      if (prev == kUninitialized) {
+        // If this is the first time we're reading this reporter, store its
+        // current value but don't report it in the telemetry ping, so we
+        // ignore the effect startup had on the reporter.
+        return;
+      }
+      val = aAmount - prev;
+      break;
+    }
+
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected aUnits value");
+      return;
+  }
+
+  // Note: The reference equality check here should allow the compiler to
+  // optimize this case out at compile time when we weren't given a key,
+  // while IsEmpty() or IsVoid() most likely will not.
+  if (&aKey == &VoidCString()) {
+    Telemetry::Accumulate(aId, val);
+  } else {
+    Telemetry::Accumulate(aId, aKey, val);
+  }
+}
+
+nsresult MemoryTelemetry::GatherReports(
+    const std::function<void()>& aCompletionCallback) {
+  RefPtr<nsMemoryReporterManager> mgr = nsMemoryReporterManager::GetOrCreate();
+  MOZ_DIAGNOSTIC_ASSERT(mgr);
+  NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
+
+  auto startTime = TimeStamp::Now();
+
+#define RECORD(id, metric, units)                                       \
+  do {                                                                  \
+    int64_t amt;                                                        \
+    nsresult rv = mgr->Get##metric(&amt);                               \
+    if (!NS_WARN_IF(NS_FAILED(rv))) {                                   \
+      HandleMemoryReport(Telemetry::id, nsIMemoryReporter::units, amt); \
+    }                                                                   \
+  } while (0)
+
+  // GHOST_WINDOWS is opt-out as of Firefox 55
+  RECORD(GHOST_WINDOWS, GhostWindows, UNITS_COUNT);
+
+  if (!Telemetry::CanRecordReleaseData()) {
+    return NS_OK;
+  }
+
+  // Get memory measurements from distinguished amount attributes.  We used
+  // to measure "explicit" too, but it could cause hangs, and the data was
+  // always really noisy anyway.  See bug 859657.
+  //
+  // test_TelemetrySession.js relies on some of these histograms being
+  // here.  If you remove any of the following histograms from here, you'll
+  // have to modify test_TelemetrySession.js:
+  //
+  //   * MEMORY_TOTAL,
+  //   * MEMORY_JS_GC_HEAP, and
+  //   * MEMORY_JS_COMPARTMENTS_SYSTEM.
+  //
+  // The distinguished amount attribute names don't match the telemetry id
+  // names in some cases due to a combination of (a) historical reasons, and
+  // (b) the fact that we can't change telemetry id names without breaking
+  // data continuity.
+
+  // Collect cheap or main-thread only metrics synchronously, on the main
+  // thread.
+  RECORD(MEMORY_JS_GC_HEAP, JSMainRuntimeGCHeap, UNITS_BYTES);
+  RECORD(MEMORY_JS_COMPARTMENTS_SYSTEM, JSMainRuntimeRealmsSystem, UNITS_COUNT);
+  RECORD(MEMORY_JS_COMPARTMENTS_USER, JSMainRuntimeRealmsUser, UNITS_COUNT);
+  RECORD(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, ImagesContentUsedUncompressed,
+         UNITS_BYTES);
+  RECORD(MEMORY_STORAGE_SQLITE, StorageSQLite, UNITS_BYTES);
+#ifdef XP_WIN
+  RECORD(LOW_MEMORY_EVENTS_VIRTUAL, LowMemoryEventsVirtual,
+         UNITS_COUNT_CUMULATIVE);
+  RECORD(LOW_MEMORY_EVENTS_COMMIT_SPACE, LowMemoryEventsCommitSpace,
+         UNITS_COUNT_CUMULATIVE);
+  RECORD(LOW_MEMORY_EVENTS_PHYSICAL, LowMemoryEventsPhysical,
+         UNITS_COUNT_CUMULATIVE);
+#endif
+#if defined(XP_LINUX) && !defined(ANDROID)
+  RECORD(PAGE_FAULTS_HARD, PageFaultsHard, UNITS_COUNT_CUMULATIVE);
+#endif
+
+  RefPtr<Runnable> completionRunnable;
+  if (aCompletionCallback) {
+    completionRunnable = NS_NewRunnableFunction(__func__, aCompletionCallback);
+  }
+
+  // Collect expensive metrics that can be calculated off-main-thread
+  // asynchronously, on a background thread.
+  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+      "MemoryTelemetry::GatherReports", [mgr, completionRunnable]() mutable {
+        RECORD(MEMORY_VSIZE, Vsize, UNITS_BYTES);
+#if !defined(HAVE_64BIT_BUILD) || !defined(XP_WIN)
+        RECORD(MEMORY_VSIZE_MAX_CONTIGUOUS, VsizeMaxContiguous, UNITS_BYTES);
+#endif
+        RECORD(MEMORY_RESIDENT_FAST, ResidentFast, UNITS_BYTES);
+        RECORD(MEMORY_UNIQUE, ResidentUnique, UNITS_BYTES);
+        RECORD(MEMORY_HEAP_ALLOCATED, HeapAllocated, UNITS_BYTES);
+        RECORD(MEMORY_HEAP_OVERHEAD_FRACTION, HeapOverheadFraction,
+               UNITS_PERCENTAGE);
+
+        if (completionRunnable) {
+          NS_DispatchToMainThread(completionRunnable.forget(),
+                                  NS_DISPATCH_NORMAL);
+        }
+      });
+
+#undef RECORD
+
+  nsresult rv = mThreadPool->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  // If we're running in the parent process, collect data from all processes for
+  // the MEMORY_TOTAL histogram.
+  if (XRE_IsParentProcess() && !mTotalMemoryGatherer) {
+    mTotalMemoryGatherer = new TotalMemoryGatherer();
+    mTotalMemoryGatherer->Begin(mThreadPool);
+  }
+
+  Telemetry::AccumulateTimeDelta(
+      Telemetry::HistogramID::TELEMETRY_MEMORY_REPORTER_MS, startTime,
+      TimeStamp::Now());
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(MemoryTelemetry::TotalMemoryGatherer, nsITimerCallback)
+
+/**
+ * Polls all child processes for their unique set size, and populates the
+ * MEMORY_TOTAL and MEMORY_DISTRIBUTION_AMONG_CONTENT histograms with the
+ * results.
+ */
+void MemoryTelemetry::TotalMemoryGatherer::Begin(nsIEventTarget* aThreadPool) {
+  nsCOMPtr<nsISerialEventTarget> target =
+      SystemGroup::EventTargetFor(TaskCategory::Other);
+
+  nsTArray<ContentParent*> parents;
+  ContentParent::GetAll(parents);
+  for (auto& parent : parents) {
+    mRemainingChildCount++;
+    parent->SendGetMemoryUniqueSetSize()->Then(
+        target, "TotalMemoryGather::Begin", this,
+        &TotalMemoryGatherer::CollectResult, &TotalMemoryGatherer::OnFailure);
+  }
+
+  mChildSizes.SetCapacity(mRemainingChildCount);
+
+  RefPtr<TotalMemoryGatherer> self{this};
+
+  aThreadPool->Dispatch(
+      NS_NewRunnableFunction(
+          "TotalMemoryGather::Begin",
+          [self]() {
+            RefPtr<nsMemoryReporterManager> mgr =
+                nsMemoryReporterManager::GetOrCreate();
+            MOZ_RELEASE_ASSERT(mgr);
+
+            NS_DispatchToMainThread(NewRunnableMethod<int64_t>(
+                "TotalMemoryGather::CollectParentSize", self,
+                &TotalMemoryGatherer::CollectParentSize, mgr->ResidentFast()));
+          }),
+      NS_DISPATCH_NORMAL);
+
+  NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
+                          kTotalMemoryCollectorTimeout,
+                          nsITimer::TYPE_ONE_SHOT);
+}
+
+nsresult MemoryTelemetry::TotalMemoryGatherer::MaybeFinish() {
+  // If we timed out waiting for a response from any child, we don't report
+  // anything for this attempt.
+  if (!mTimeout || !mHaveParentSize || mRemainingChildCount) {
+    return NS_OK;
+  }
+
+  mTimeout = nullptr;
+  MemoryTelemetry::Get().mTotalMemoryGatherer = nullptr;
+
+  HandleMemoryReport(Telemetry::MEMORY_TOTAL, nsIMemoryReporter::UNITS_BYTES,
+                     mTotalMemory);
+
+  if (mChildSizes.Length() > 1) {
+    int32_t tabsCount;
+    MOZ_TRY_VAR(tabsCount, GetOpenTabsCount());
+
+    nsCString key;
+    if (tabsCount <= 10) {
+      key = "0 - 10 tabs";
+    } else if (tabsCount <= 500) {
+      key = "11 - 500 tabs";
+    } else {
+      key = "more tabs";
+    }
+
+    // Mean of the USS of all the content processes.
+    int64_t mean = 0;
+    for (auto size : mChildSizes) {
+      mean += size;
+    }
+    mean /= mChildSizes.Length();
+
+    // Absolute error of USS for each content process, normalized by the mean
+    // (*100 to get it in percentage). 20% means for a content process that it
+    // is using 20% more or 20% less than the mean.
+    for (auto size : mChildSizes) {
+      int64_t diff = llabs(size - mean) * 100 / mean;
+
+      HandleMemoryReport(Telemetry::MEMORY_DISTRIBUTION_AMONG_CONTENT,
+                         nsIMemoryReporter::UNITS_COUNT, diff, key);
+    }
+  }
+
+  // This notification is for testing only.
+  if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
+    obs->NotifyObservers(nullptr, "gather-memory-telemetry-finished", nullptr);
+  }
+
+  return NS_OK;
+}
+
+void MemoryTelemetry::TotalMemoryGatherer::CollectParentSize(
+    int64_t aResident) {
+  mTotalMemory += aResident;
+  mHaveParentSize = true;
+
+  MaybeFinish();
+}
+
+void MemoryTelemetry::TotalMemoryGatherer::CollectResult(int64_t aChildUSS) {
+  mChildSizes.AppendElement(aChildUSS);
+
+  mTotalMemory += aChildUSS;
+  mRemainingChildCount--;
+
+  MaybeFinish();
+}
+
+void MemoryTelemetry::TotalMemoryGatherer::OnFailure(
+    mozilla::ipc::ResponseRejectReason aReason) {
+  // Treat failure of any request the same as a timeout.
+  Notify(nullptr);
+}
+
+nsresult MemoryTelemetry::TotalMemoryGatherer::Notify(nsITimer* aTimer) {
+  // Set mTimeout null to indicate the timeout has fired. After this, all
+  // results for this attempt will be ignored.
+  mTimeout = nullptr;
+  MemoryTelemetry::Get().mTotalMemoryGatherer = nullptr;
+  return NS_OK;
+}
+
+/* static */ Result<uint32_t, nsresult> MemoryTelemetry::GetOpenTabsCount() {
+  nsresult rv;
+
+  nsCOMPtr<nsIWindowMediator> windowMediator(
+      do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
+  MOZ_TRY(rv);
+
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  MOZ_TRY(windowMediator->GetEnumerator(u"navigator:browser",
+                                        getter_AddRefs(enumerator)));
+
+  uint32_t total = 0;
+  for (auto& window : SimpleEnumerator<nsIDOMChromeWindow>(enumerator)) {
+    nsCOMPtr<nsIBrowserDOMWindow> browserWin;
+    MOZ_TRY(window->GetBrowserDOMWindow(getter_AddRefs(browserWin)));
+
+    NS_ENSURE_TRUE(browserWin, Err(NS_ERROR_UNEXPECTED));
+
+    uint32_t tabCount;
+    MOZ_TRY(browserWin->GetTabCount(&tabCount));
+    total += tabCount;
+  }
+
+  return total;
+}
+
+void MemoryTelemetry::GetUniqueSetSize(
+    std::function<void(const int64_t&)>&& aCallback) {
+  mThreadPool->Dispatch(
+      NS_NewRunnableFunction(
+          "MemoryTelemetry::GetUniqueSetSize",
+          [callback = std::move(aCallback)]() mutable {
+            RefPtr<nsMemoryReporterManager> mgr =
+                nsMemoryReporterManager::GetOrCreate();
+            MOZ_RELEASE_ASSERT(mgr);
+
+            int64_t uss = mgr->ResidentUnique();
+
+            NS_DispatchToMainThread(NS_NewRunnableFunction(
+                "MemoryTelemetry::GetUniqueSetSizeResult",
+                [uss, callback = std::move(callback)]() { callback(uss); }));
+          }),
+      NS_DISPATCH_NORMAL);
+}
+
+nsresult MemoryTelemetry::Observe(nsISupports* aSubject, const char* aTopic,
+                                  const char16_t* aData) {
+  if (strcmp(aTopic, kTopicCycleCollectorBegin) == 0) {
+    auto now = TimeStamp::Now();
+    if (!mLastPoll.IsNull() &&
+        (now - mLastPoll).ToMilliseconds() < kTelemetryInterval) {
+      return NS_OK;
+    }
+
+    mLastPoll = now;
+
+    NS_IdleDispatchToCurrentThread(NewRunnableMethod<std::function<void()>>(
+        "MemoryTelemetry::GatherReports", this, &MemoryTelemetry::GatherReports,
+        nullptr));
+  } else if (strcmp(aTopic, "content-child-shutdown") == 0) {
+    if (nsCOMPtr<nsITelemetry> telemetry =
+            do_GetService("@mozilla.org/base/telemetry;1")) {
+      telemetry->FlushBatchedChildTelemetry();
+    }
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/base/MemoryTelemetry.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_MemoryTelemetry_h
+#define mozilla_MemoryTelemetry_h
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Result.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+#include <functional>
+
+namespace mozilla {
+
+namespace ipc {
+enum class ResponseRejectReason;
+}
+
+/**
+ * Periodically gathers memory usage metrics after cycle collection, and
+ * populates telemetry histograms with their values.
+ */
+class MemoryTelemetry final : public nsIObserver,
+                              public nsSupportsWeakReference {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static MemoryTelemetry& Get();
+
+  nsresult GatherReports(const std::function<void()>& aCompletionCallback = nullptr);
+
+  void GetUniqueSetSize(std::function<void(const int64_t&)>&& aCallback);
+
+  /**
+   * Does expensive initialization, which should happen only after startup has
+   * completed, and the event loop is idle.
+   */
+  nsresult DelayedInit();
+
+  nsresult Shutdown();
+
+ private:
+  MemoryTelemetry();
+
+  ~MemoryTelemetry() = default;
+
+  void Init();
+
+  static Result<uint32_t, nsresult> GetOpenTabsCount();
+
+  class TotalMemoryGatherer final : public nsITimerCallback {
+   public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSITIMERCALLBACK
+
+    TotalMemoryGatherer() {}
+
+    void CollectParentSize(int64_t aResident);
+    void CollectResult(int64_t aChildUSS);
+    void OnFailure(ipc::ResponseRejectReason aReason);
+
+    void Begin(nsIEventTarget* aThreadPool);
+
+   private:
+    ~TotalMemoryGatherer() = default;
+
+    nsresult MaybeFinish();
+
+    nsCOMPtr<nsITimer> mTimeout;
+
+    nsTArray<int64_t> mChildSizes;
+
+    int64_t mTotalMemory = 0;
+    uint32_t mRemainingChildCount = 0;
+
+    bool mHaveParentSize = false;
+  };
+
+  nsCOMPtr<nsIEventTarget> mThreadPool;
+  RefPtr<TotalMemoryGatherer> mTotalMemoryGatherer;
+
+  TimeStamp mLastPoll{};
+};
+
+}  // namespace mozilla
+
+#endif  // defined mozilla_MemoryTelemetry_h
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -114,16 +114,17 @@ EXPORTS.mozilla += [
     'ErrorNames.h',
     'HoldDropJSObjects.h',
     'IntentionalCrash.h',
     'JSObjectHolder.h',
     'Logging.h',
     'MemoryInfo.h',
     'MemoryMapping.h',
     'MemoryReportingProcess.h',
+    'MemoryTelemetry.h',
     'nsMemoryInfoDumper.h',
     'NSPRLogModulesParser.h',
     'OwningNonNull.h',
     'SizeOfState.h',
     'StaticMonitor.h',
     'StaticMutex.h',
     'StaticPtr.h',
     'TupleCycleCollection.h',
@@ -145,16 +146,17 @@ UNIFIED_SOURCES += [
     'DebuggerOnGCRunnable.cpp',
     'DeferredFinalize.cpp',
     'ErrorNames.cpp',
     'HoldDropJSObjects.cpp',
     'JSObjectHolder.cpp',
     'LogCommandLineHandler.cpp',
     'Logging.cpp',
     'LogModulePrefWatcher.cpp',
+    'MemoryTelemetry.cpp',
     'nsClassInfoImpl.cpp',
     'nsCOMPtr.cpp',
     'nsConsoleMessage.cpp',
     'nsConsoleService.cpp',
     'nsCRTGlue.cpp',
     'nsCycleCollectionParticipant.cpp',
     'nsCycleCollector.cpp',
     'nsCycleCollectorTraceJSHelpers.cpp',
@@ -212,16 +214,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../build',
     '/dom/base',
+    '/js/xpconnect/loader', # for mozJSComponentLoader.h
     '/mfbt',
     '/xpcom/ds',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     CXXFLAGS += CONFIG['TK_CFLAGS']
 
 if CONFIG['ENABLE_CLANG_PLUGIN'] and CONFIG['CC_TYPE'] == 'clang-cl':
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -415,22 +415,16 @@ interface nsIMemoryReporterManager : nsI
   /*
    * These attributes indicate DMD's status. "Enabled" means enabled at
    * build-time.
    */
   [infallible] readonly attribute boolean isDMDEnabled;
   [infallible] readonly attribute boolean isDMDRunning;
 
   /*
-   * Asynchronously gets attribute 'heapAllocated'. The value is returned in
-   * the callback argument.
-   */
-  [must_use] void getHeapAllocatedAsync(in nsIHeapAllocatedCallback callback);
-
-  /*
    * Run a series of GC/CC's in an attempt to minimize the application's memory
    * usage.  When we're finished, we invoke the given runnable if it's not
    * null.
    */
   [must_use] void minimizeMemoryUsage(in nsIRunnable callback);
 
   /*
    * Measure the memory that is known to be owned by this tab, split up into
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -2399,59 +2399,16 @@ nsMemoryReporterManager::GetHeapAllocate
   *aAmount = stats.allocated;
   return NS_OK;
 #else
   *aAmount = 0;
   return NS_ERROR_NOT_AVAILABLE;
 #endif
 }
 
-NS_IMETHODIMP
-nsMemoryReporterManager::GetHeapAllocatedAsync(
-    nsIHeapAllocatedCallback* aCallback) {
-#ifdef HAVE_JEMALLOC_STATS
-  if (!mThreadPool) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  RefPtr<nsIMemoryReporterManager> self{this};
-  nsMainThreadPtrHandle<nsIHeapAllocatedCallback> mainThreadCallback(
-      new nsMainThreadPtrHolder<nsIHeapAllocatedCallback>(
-          "HeapAllocatedCallback", aCallback));
-
-  nsCOMPtr<nsIRunnable> getHeapAllocatedRunnable = NS_NewRunnableFunction(
-      "nsMemoryReporterManager::GetHeapAllocatedAsync",
-      [self, mainThreadCallback]() mutable {
-        MOZ_ASSERT(!NS_IsMainThread());
-
-        int64_t heapAllocated = 0;
-        nsresult rv = self->GetHeapAllocated(&heapAllocated);
-
-        nsCOMPtr<nsIRunnable> resultCallbackRunnable = NS_NewRunnableFunction(
-            "nsMemoryReporterManager::GetHeapAllocatedAsync",
-            [mainThreadCallback, heapAllocated, rv]() mutable {
-              MOZ_ASSERT(NS_IsMainThread());
-
-              if (NS_FAILED(rv)) {
-                mainThreadCallback->Callback(0);
-                return;
-              }
-
-              mainThreadCallback->Callback(heapAllocated);
-            });  // resultCallbackRunnable.
-
-        Unused << NS_DispatchToMainThread(resultCallbackRunnable);
-      });  // getHeapAllocatedRunnable.
-
-  return mThreadPool->Dispatch(getHeapAllocatedRunnable, NS_DISPATCH_NORMAL);
-#else
-  return NS_ERROR_NOT_AVAILABLE;
-#endif
-}
-
 // This has UNITS_PERCENTAGE, so it is multiplied by 100x.
 NS_IMETHODIMP
 nsMemoryReporterManager::GetHeapOverheadFraction(int64_t* aAmount) {
 #ifdef HAVE_JEMALLOC_STATS
   jemalloc_stats_t stats;
   jemalloc_stats(&stats);
   *aAmount = HeapOverheadFraction(&stats);
   return NS_OK;