Merge autoland to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Tue, 02 Apr 2019 11:32:59 +0300
changeset 467515 ea09774456976ada64ed753a00724ef6bc6be62f
parent 467423 3ebf6f2a81387d7087ef235c3ce60452baf550f3 (current diff)
parent 467514 661bafa045c4206511058bc760343318df4a751b (diff)
child 467516 41e4fc459ec96f96d1e49a203717a7d233f075ce
child 467563 bd1a81666a6bb95b89eb72dce23a340335483ac2
push id35799
push usercbrindusan@mozilla.com
push dateTue, 02 Apr 2019 08:35:12 +0000
treeherdermozilla-central@ea0977445697 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
ea0977445697 / 68.0a1 / 20190402083512 / files
nightly linux64
ea0977445697 / 68.0a1 / 20190402083512 / files
nightly mac
ea0977445697 / 68.0a1 / 20190402083512 / files
nightly win32
ea0977445697 / 68.0a1 / 20190402083512 / files
nightly win64
ea0977445697 / 68.0a1 / 20190402083512 / 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 autoland to mozilla-central. a=merge
build/build-clang/clang-7-android.json
build/build-clang/clang-7-linux64.json
build/build-clang/clang-7-macosx64.json
build/build-clang/clang-trunk-mingw.json
build/build-clang/r350774.patch
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
taskcluster/scripts/misc/build-clang-7-android.sh
taskcluster/scripts/misc/build-clang-7-linux-macosx-cross.sh
taskcluster/scripts/misc/build-clang-7-linux.sh
taskcluster/scripts/misc/build-clang-trunk-mingw.sh
testing/mozharness/configs/unittests/win_taskcluster_unittest.py
testing/mozharness/mozharness/mozilla/aws.py
testing/mozharness/scripts/release/push-candidate-to-releases.py
xpcom/base/nsIID.h
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -181,21 +181,27 @@ MARKUPMAP(h6, New_HyperText, roles::HEAD
 MARKUPMAP(hr,
           [](Element* aElement, Accessible* aContext) -> Accessible* {
             return new HTMLHRAccessible(aElement, aContext->Document());
           },
           0)
 
 MARKUPMAP(input,
           [](Element* aElement, Accessible* aContext) -> Accessible* {
+            // TODO(emilio): This would be faster if it used
+            // HTMLInputElement's already-parsed representation.
             if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                       nsGkAtoms::checkbox, eIgnoreCase)) {
               return new CheckboxAccessible(aElement, aContext->Document());
             }
             if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                                      nsGkAtoms::image, eIgnoreCase)) {
+              return new HTMLButtonAccessible(aElement, aContext->Document());
+            }
+            if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                       nsGkAtoms::radio, eIgnoreCase)) {
               return new HTMLRadioButtonAccessible(aElement,
                                                    aContext->Document());
             }
             if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                       nsGkAtoms::time, eIgnoreCase)) {
               return new EnumRoleAccessible<roles::GROUPING>(
                   aElement, aContext->Document());
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -711,16 +711,17 @@
       // HTML:input@type="image"
 
       obj = {
         role: ROLE_PUSHBUTTON,
         absentStates: STATE_DEFAULT,
         actions: "press",
       };
       testElm("input_image", obj);
+      testElm("input_image_display", obj);
       testElm("input_submit", obj);
 
       obj = {
         role: ROLE_PUSHBUTTON,
         actions: "press",
         states: STATE_DEFAULT,
       };
       testElm("input_image_default", obj);
@@ -1646,16 +1647,17 @@
   <p id="i_container">normal<i>italic</i></p>
   <img id="img" src="../moz.png">
 
   <input id="input_button" type="button" value="Button">
   <input id="input_checkbox" type="checkbox">
   <input id="input_checkbox_true" type="checkbox" checked>
   <input id="input_file" type="file">
   <input id="input_image" type="image">
+  <input id="input_image_display" type="image" style="display: block">
   <form>
     <input id="input_image_default" type="image">
   </form>
   <input id="input_submit" type="submit">
   <form>
     <input id="input_submit_default" type="submit">
   </form>
   <input id="input_number" type="number" value="44">
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -11,18 +11,16 @@ const {ActorChild} = ChromeUtils.import(
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
-XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
-
 XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
   return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
 });
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle("chrome://branding/locale/brand.properties");
 });
 XPCOMUtils.defineLazyPreferenceGetter(this, "mitmErrorPageEnabled",
   "browser.security.newcerterrorpage.mitm.enabled");
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -1118,17 +1118,17 @@ var ContentBlocking = {
       this.iconBox.removeAttribute("animate");
     // Only play the animation when the shield is not already shown on the page (the visibility
     // of the shield based on this onSecurityChange be determined afterwards).
     } else if (anyBlocking && !this.iconBox.hasAttribute("active")) {
       this.iconBox.setAttribute("animate", "true");
 
       if (!isBrowserPrivate) {
         let introCount = Services.prefs.getIntPref(this.prefIntroCount);
-        if (introCount < this.MAX_INTROS) {
+        if (introCount < this.MAX_INTROS && !this.anyOtherWindowHasTour()) {
           Services.prefs.setIntPref(this.prefIntroCount, ++introCount);
           Services.prefs.savePrefFile(null);
           this.showIntroPanel();
         }
       }
     }
 
     // We consider the shield state "active" when some kind of blocking activity
@@ -1184,16 +1184,26 @@ var ContentBlocking = {
 
     if (cryptominingBlocking) {
       this.cryptominersHistogramAdd("blocked");
     } else if (cryptominingAllowing) {
       this.cryptominersHistogramAdd("allowed");
     }
   },
 
+  // Check if any existing window has a UItour initiated, both showing and hidden.
+  anyOtherWindowHasTour() {
+    for (let win of BrowserWindowTracker.orderedWindows) {
+      if (win != window && UITour.tourBrowsersByWindow.has(win)) {
+        return true;
+      }
+    }
+    return false;
+  },
+
   disableForCurrentPage() {
     let baseURI = this._baseURIForChannelClassifier;
 
     // Add the current host in the 'trackingprotection' consumer of
     // the permission manager using a normalized URI. This effectively
     // places this host on the tracking protection allowlist.
     if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
       PrivateBrowsingUtils.addToTrackingAllowlist(baseURI);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -352,16 +352,17 @@ reason = depends on UpdateUtils .Locale
 support-files =
   test_remoteTroubleshoot.html
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_remoteWebNavigation_postdata.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_removeTabsToTheEnd.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_restore_isAppTab.js]
+skip-if = !crashreporter # test requires crashreporter due to 1536221
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_link-perwindowpb.js]
 skip-if = (e10s && debug && os == "win") || verify # Bug 1280505
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_private_link_perwindowpb.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_link_when_window_navigates.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -87,13 +87,13 @@ tags = blocklist
 skip-if = !e10s
 tags = blocklist
 [browser_enable_DRM_prompt.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533164
 [browser_private_browsing_eme_persistent_state.js]
 [browser_globalplugin_crashinfobar.js]
 skip-if = !crashreporter
 [browser_pluginCrashCommentAndURL.js]
-skip-if = !crashreporter
+skip-if = !crashreporter || (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [browser_pluginCrashReportNonDeterminism.js]
-skip-if = !crashreporter
+skip-if = !crashreporter || (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [browser_private_clicktoplay.js]
 [browser_subframe_access_hidden_plugins.js]
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -34,16 +34,17 @@ skip-if = os != 'mac'
 [browser_policy_block_about_support.js]
 [browser_policy_block_set_desktop_background.js]
 [browser_policy_bookmarks.js]
 [browser_policy_clear_blocked_cookies.js]
 [browser_policy_cookie_settings.js]
 [browser_policy_default_browser_check.js]
 [browser_policy_disable_feedback_commands.js]
 [browser_policy_disable_flash_plugin.js]
+skip-if = (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [browser_policy_disable_fxaccounts.js]
 skip-if = (verify && debug && (os == 'mac'))
 [browser_policy_disable_masterpassword.js]
 [browser_policy_disable_pocket.js]
 [browser_policy_disable_popup_blocker.js]
 [browser_policy_disable_privatebrowsing.js]
 [browser_policy_disable_profile_reset.js]
 [browser_policy_disable_profile_import.js]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -152,17 +152,17 @@ skip-if = (verify && debug && (os == 'ma
 [browser_ext_popup_api_injection.js]
 [browser_ext_popup_background.js]
 [browser_ext_popup_corners.js]
 [browser_ext_popup_focus.js]
 disabled = bug 1438663
 [browser_ext_popup_sendMessage.js]
 [browser_ext_popup_shutdown.js]
 [browser_ext_port_disconnect_on_crash.js]
-skip-if = !e10s || !crashreporter # the tab's process is killed during the test. Without e10s the parent process would die too.
+skip-if = !e10s || !crashreporter || (processor == 'aarch64' && os == 'win') # the tab's process is killed during the test. Without e10s the parent process would die too, aarch64 due to 1538785
 [browser_ext_port_disconnect_on_window_close.js]
 [browser_ext_runtime_openOptionsPage.js]
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_search.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_sessions_forgetClosedTab.js]
 [browser_ext_sessions_forgetClosedWindow.js]
 [browser_ext_sessions_incognito.js]
--- a/browser/components/preferences/connection.js
+++ b/browser/components/preferences/connection.js
@@ -40,17 +40,24 @@ Preferences.addAll([
   { id: "network.trr.custom_uri", "type": "string" },
 ]);
 
 window.addEventListener("DOMContentLoaded", () => {
   Preferences.get("network.proxy.type").on("change",
     gConnectionsDialog.proxyTypeChanged.bind(gConnectionsDialog));
   Preferences.get("network.proxy.socks_version").on("change",
     gConnectionsDialog.updateDNSPref.bind(gConnectionsDialog));
-  gConnectionsDialog.initDnsOverHttpsUI();
+
+  // wait until the network.trr prefs are added before init'ing the UI for them
+  gConnectionsDialog.uiReady = new Promise(resolve => {
+    gConnectionsDialog._initialPrefsAdded = resolve;
+  }).then(() => {
+    delete gConnectionsDialog._initialPrefsAdded;
+    gConnectionsDialog.initDnsOverHttpsUI();
+  });
 
   document
     .getElementById("disableProxyExtension")
     .addEventListener(
       "command", makeDisableControllingExtension(
         PREF_SETTING_TYPE, PROXY_KEY).bind(gConnectionsDialog));
   gConnectionsDialog.updateProxySettingsUI();
   initializeProxyUI(gConnectionsDialog);
@@ -297,19 +304,23 @@ var gConnectionsDialog = {
     // values outside 1:4 are considered falsey/disabled in this context
     let trrPref = Preferences.get("network.trr.mode");
     let enabled = trrPref.value > 0 && trrPref.value < 5;
     return enabled;
   },
 
   readDnsOverHttpsMode() {
     // called to update checked element property to reflect current pref value
+    // this is the first signal we get when the prefs are added, so lazy-init
     let enabled = this.isDnsOverHttpsEnabled();
     let uriPref = Preferences.get("network.trr.uri");
     uriPref.disabled = !enabled || this.isDnsOverHttpsLocked();
+    if (this._initialPrefsAdded) {
+      this._initialPrefsAdded();
+    }
     return enabled;
   },
 
   writeDnsOverHttpsMode() {
     // called to update pref with user change
     let trrModeCheckbox = document.getElementById("networkDnsOverHttps");
     // we treat checked/enabled as mode 2
     return trrModeCheckbox.checked ? 2 : 0;
--- a/browser/components/preferences/fonts.js
+++ b/browser/components/preferences/fonts.js
@@ -10,17 +10,17 @@
 const kDefaultFontType          = "font.default.%LANG%";
 const kFontNameFmtSerif         = "font.name.serif.%LANG%";
 const kFontNameFmtSansSerif     = "font.name.sans-serif.%LANG%";
 const kFontNameFmtMonospace     = "font.name.monospace.%LANG%";
 const kFontNameListFmtSerif     = "font.name-list.serif.%LANG%";
 const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
 const kFontNameListFmtMonospace = "font.name-list.monospace.%LANG%";
 const kFontSizeFmtVariable      = "font.size.variable.%LANG%";
-const kFontSizeFmtFixed         = "font.size.fixed.%LANG%";
+const kFontSizeFmtFixed         = "font.size.monospace.%LANG%";
 const kFontMinSizeFmt           = "font.minimum-size.%LANG%";
 
 document.documentElement.addEventListener("dialoghelp", window.top.openPrefsHelp);
 
 Preferences.addAll([
   { id: "font.language.group", type: "wstring" },
   { id: "browser.display.use_document_fonts", type: "int" },
   { id: "intl.charset.fallback.override", type: "string" },
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -30,16 +30,17 @@ skip-if = (os == 'win' && (processor == 
 [browser_search_subdialogs_within_preferences_8.js]
 [browser_search_subdialogs_within_preferences_site_data.js]
 [browser_bug795764_cachedisabled.js]
 [browser_bug1018066_resetScrollPosition.js]
 [browser_bug1020245_openPreferences_to_paneContent.js]
 [browser_bug1184989_prevent_scrolling_when_preferences_flipped.js]
 support-files =
   browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
+[browser_cookie_exceptions_addRemove.js]
 [browser_cert_export.js]
 [browser_engines.js]
 [browser_change_app_handler.js]
 skip-if = os != "win" # Windows-specific handler application selection dialog
 [browser_checkspelling.js]
 [browser_cloud_storage.js]
 [browser_connection.js]
 [browser_connection_bug388287.js]
--- a/browser/components/preferences/in-content/tests/browser_connection_dnsoverhttps.js
+++ b/browser/components/preferences/in-content/tests/browser_connection_dnsoverhttps.js
@@ -59,16 +59,17 @@ async function testWithProperties(props,
   if (props.hasOwnProperty(TRR_CUSTOM_URI_PREF)) {
     Services.prefs.setStringPref(TRR_CUSTOM_URI_PREF, props[TRR_CUSTOM_URI_PREF]);
   }
   if (props.hasOwnProperty(TRR_URI_PREF)) {
     Services.prefs.setStringPref(TRR_URI_PREF, props[TRR_URI_PREF]);
   }
 
   let dialog = await openConnectionsSubDialog();
+  await dialog.uiReady;
   info((Date.now() - startTime) + ": testWithProperties: connections dialog now open");
   let doc = dialog.document;
   let win = doc.ownerGlobal;
   let dialogClosingPromise = BrowserTestUtils.waitForEvent(doc.documentElement,
                                                            "dialogclosing");
   let modeCheckbox = doc.querySelector(modeCheckboxSelector);
   let uriTextbox = doc.querySelector(uriTextboxSelector);
   let uriPrefChangedPromise;
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_cookie_exceptions_addRemove.js
@@ -0,0 +1,241 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
+
+async function openCookiesDialog(doc) {
+  let cookieExceptionsButton = doc.getElementById("cookieExceptions");
+  ok(cookieExceptionsButton, "cookieExceptionsButton found");
+  let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
+  cookieExceptionsButton.click();
+  let dialog = await dialogPromise;
+  return dialog;
+}
+
+function checkCookiesDialog(dialog) {
+  ok(dialog, "dialog loaded");
+  let buttonIds = ["removePermission", "removeAllPermissions", "btnApplyChanges"];
+
+  for (let buttonId of buttonIds) {
+    let button = dialog.document.getElementById(buttonId);
+    ok(button, `${buttonId} found`);
+  }
+
+  let cancelButton =
+    dialog.document.getElementsByClassName("actionButtons")[1].children[0];
+
+  is(cancelButton.getAttribute("label"), "Cancel", "cancelButton found");
+}
+
+function addNewPermission(websiteAddress, dialog) {
+  let url = dialog.document.getElementById("url");
+  let buttonDialog = dialog.document.getElementById("btnBlock");
+  let permissionsBox = dialog.document.getElementById("permissionsBox");
+  let currentPermissions = permissionsBox.itemCount;
+
+  url.value = websiteAddress;
+  url.dispatchEvent(new Event("input", {bubbles: true}));
+  is(buttonDialog.hasAttribute("disabled"), false,
+     "When the user add an url the button should be clickable");
+  buttonDialog.click();
+
+  is(permissionsBox.itemCount, currentPermissions + 1,
+     "Website added in url should be in the list");
+}
+
+async function cleanList(dialog) {
+  let removeAllButton = dialog.document.getElementById("removeAllPermissions");
+  if (!removeAllButton.hasAttribute("disabled")) removeAllButton.click();
+}
+
+function addData(websites, dialog) {
+  for (let website of websites) {
+    addNewPermission(website, dialog);
+  }
+}
+
+function deletePermission(permission, dialog) {
+  let permissionsBox = dialog.document.getElementById("permissionsBox");
+  let elements = permissionsBox.getElementsByAttribute("origin", permission);
+  is(elements.length, 1, "It should find only one entry");
+  permissionsBox.selectItem(elements[0]);
+  let removePermissionButton = dialog.document.getElementById("removePermission");
+  is(removePermissionButton.hasAttribute("disabled"), false,
+     "The button should be clickable to remove selected item");
+  removePermissionButton.click();
+}
+
+function save(dialog) {
+  let saveButton = dialog.document.getElementById("btnApplyChanges");
+  saveButton.click();
+}
+
+function cancel(dialog) {
+  let cancelButton =
+    dialog.document.getElementsByClassName("actionButtons")[1].children[0];
+  is(cancelButton.getAttribute("label"), "Cancel", "cancelButton found");
+  cancelButton.click();
+}
+
+async function checkExpected(expected, doc) {
+  let dialog = await openCookiesDialog(doc);
+  let permissionsBox = dialog.document.getElementById("permissionsBox");
+
+  is(permissionsBox.itemCount, expected.length,
+     `There should be ${expected.length} elements in the list`);
+
+  for (let website of expected) {
+    let elements = permissionsBox.getElementsByAttribute("origin", website);
+    is(elements.length, 1, "It should find only one entry");
+  }
+  return dialog;
+}
+
+async function runTest(test, websites, doc) {
+  let dialog = await openCookiesDialog(doc);
+  checkCookiesDialog(dialog);
+
+  if (test.needPreviousData) {
+    addData(websites, dialog);
+    save(dialog);
+    dialog = await openCookiesDialog(doc);
+  }
+
+  for (let step of test.steps) {
+    switch (step) {
+      case "addNewPermission":
+        addNewPermission(test.newData, dialog);
+        break;
+      case "deletePermission":
+        deletePermission(test.newData, dialog);
+        break;
+      case "deleteAllPermission":
+        await cleanList(dialog);
+        break;
+      case "save":
+        save(dialog);
+        break;
+      case "cancel":
+        cancel(dialog);
+        break;
+      case "openPane":
+        dialog = await openCookiesDialog(doc);
+        break;
+      default:
+        // code block
+    }
+  }
+  dialog = await checkExpected(test.expected, doc);
+  await cleanList(dialog);
+  save(dialog);
+}
+
+add_task(async function checkPermissions() {
+  await openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true});
+  let win = gBrowser.selectedBrowser.contentWindow;
+  let doc = win.document;
+  let websites = ["http://test1.com", "http://test2.com"];
+
+  let tests = [{
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save"],
+    "expected": ["https://mytest.com"], // when open the pane again it should find this in the list
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "cancel"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "deletePermission", "save"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "deletePermission", "cancel"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save", "openPane", "deletePermission", "save"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save", "openPane", "deletePermission", "cancel"],
+    "expected": ["https://mytest.com"],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "deleteAllPermission", "save"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "deleteAllPermission", "cancel"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save", "openPane", "deleteAllPermission", "save"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save", "openPane", "deleteAllPermission", "cancel"],
+    "expected": ["https://mytest.com"],
+  },
+  {
+    "needPreviousData": true,
+    "newData": "https://mytest.com",
+    "steps": ["deleteAllPermission", "save"],
+    "expected": [],
+  },
+  {
+    "needPreviousData": true,
+    "newData": "https://mytest.com",
+    "steps": ["deleteAllPermission", "cancel"],
+    "expected": websites,
+  },
+  {
+    "needPreviousData": true,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save"],
+    "expected": (function() {
+      let result = websites.slice();
+      result.push("https://mytest.com");
+      return result;
+    }()),
+  },
+  {
+    "needPreviousData": true,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "cancel"],
+    "expected": websites,
+  },
+  {
+    "needPreviousData": false,
+    "newData": "https://mytest.com",
+    "steps": ["addNewPermission", "save", "openPane", "deleteAllPermission", "addNewPermission",  "save"],
+    "expected": ["https://mytest.com"],
+  }];
+
+  for (let test of tests) {
+    await runTest(test, websites, doc);
+  }
+
+  gBrowser.removeCurrentTab();
+});
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -370,24 +370,24 @@ var gPermissionManager = {
   },
 
   onApplyChanges() {
     // Stop observing permission changes since we are about
     // to write out the pending adds/deletes and don't need
     // to update the UI
     this.uninit();
 
+    for (let p of this._permissionsToDelete.values()) {
+      Services.perms.removeFromPrincipal(p.principal, p.type);
+    }
+
     for (let p of this._permissionsToAdd.values()) {
       Services.perms.addFromPrincipal(p.principal, p.type, p.capability);
     }
 
-    for (let p of this._permissionsToDelete.values()) {
-      Services.perms.removeFromPrincipal(p.principal, p.type);
-    }
-
     window.close();
   },
 
   buildPermissionsList(sortCol) {
     // Clear old entries.
     let oldItems = this._list.querySelectorAll("richlistitem");
     for (let item of oldItems) {
       item.remove();
--- a/browser/config/mozconfigs/linux64/code-coverage
+++ b/browser/config/mozconfigs/linux64/code-coverage
@@ -7,11 +7,11 @@ TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
 ac_add_options --disable-install-strip
 ac_add_options --disable-elf-hack
 ac_add_options --disable-sandbox
 ac_add_options --disable-dmd
 ac_add_options --disable-profiling
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 
-export LDFLAGS="--coverage -L$topsrcdir/clang/lib/clang/7.0.1/lib/linux/"
+export LDFLAGS="--coverage -L$topsrcdir/clang/lib/clang/8.0.0/lib/linux/"
 export LIBS="-lclang_rt.profile-x86_64"
-export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads"
+export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads -Clink-dead-code -Coverflow-checks=off"
--- a/browser/config/mozconfigs/macosx64/code-coverage
+++ b/browser/config/mozconfigs/macosx64/code-coverage
@@ -3,11 +3,11 @@
 ac_add_options --disable-lto
 
 TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
 
 ac_add_options --disable-sandbox
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 
-export LDFLAGS="-coverage -L$topsrcdir/clang/lib/clang/7.0.1/lib/darwin/"
+export LDFLAGS="-coverage -L$topsrcdir/clang/lib/clang/8.0.0/lib/darwin/"
 export LIBS="-lclang_rt.profile_osx"
-export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads"
+export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads -Clink-dead-code -Coverflow-checks=off"
--- a/browser/config/mozconfigs/win64/code-coverage
+++ b/browser/config/mozconfigs/win64/code-coverage
@@ -23,12 +23,12 @@ export MOZ_PACKAGE_JSSHELL=1
 
 if [ -d "$topsrcdir/clang" ]; then
     CLANG_LIB_DIR="$(cd $topsrcdir/clang/lib/clang/* && cd lib/windows && pwd)"
 
     export LIB=$LIB:$CLANG_LIB_DIR
     export LDFLAGS="clang_rt.profile-x86_64.lib"
 fi
 
-export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads"
+export RUSTFLAGS="-Ccodegen-units=1 -Zprofile -Zno-landing-pads -Clink-dead-code -Coverflow-checks=off"
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/build/.lldbinit.in
+++ b/build/.lldbinit.in
@@ -1,2 +1,17 @@
 #filter substitution
 script topsrcdir = "@topsrcdir@"; lldb.debugger.HandleCommand("command source -s true '%s'" % os.path.join(topsrcdir, ".lldbinit"))
+
+#ifdef MOZ_WIDGET_ANDROID
+settings set symbols.enable-external-lookup true
+
+# This is where libxul.so and libmozglue.so are produced in full builds.
+settings append target.exec-search-paths @topobjdir@/toolkit/library
+settings append target.exec-search-paths @topobjdir@/mozglue/build
+
+# This is where artifact builds unpacks "crashreporter-symbols-full.zip" uncompressed ELF debug symbols.
+settings append target.debug-file-search-paths @topobjdir@/dist/crashreporter-symbols
+
+# These are specific paths encoded into Mozilla's automation outputs.
+settings append target.source-map /builds/worker/workspace/build/src/obj-firefox @topobjdir@
+settings append target.source-map /builds/worker/workspace/build/src @topsrcdir@
+#endif
rename from build/build-clang/clang-7-android.json
rename to build/build-clang/clang-8-android.json
--- a/build/build-clang/clang-7-android.json
+++ b/build/build-clang/clang-8-android.json
@@ -1,20 +1,20 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "2",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_701/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_701/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "android_targets": {
       "armv7-linux-android": {
         "ndk_toolchain": "/builds/worker/workspace/build/src/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64",
@@ -42,12 +42,11 @@
           "/builds/worker/workspace/build/src/android-ndk/sysroot/usr/include"
         ],
         "api_level": 21
       }
     },
     "patches": [
       "static-llvm-symbolizer.patch",
       "find_symbolizer_linux.patch",
-      "rename_gcov_flush_.patch",
-      "r350774.patch"
+      "rename_gcov_flush_.patch"
     ]
 }
rename from build/build-clang/clang-7-linux64.json
rename to build/build-clang/clang-8-linux64.json
--- a/build/build-clang/clang-7-linux64.json
+++ b/build/build-clang/clang-8-linux64.json
@@ -1,25 +1,24 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_701/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_701/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "patches": [
       "static-llvm-symbolizer.patch",
       "find_symbolizer_linux.patch",
       "rename_gcov_flush_.patch",
-      "r350774.patch",
       "android-mangling-error.patch"
     ]
 }
rename from build/build-clang/clang-7-macosx64.json
rename to build/build-clang/clang-8-macosx64.json
--- a/build/build-clang/clang-7-macosx64.json
+++ b/build/build-clang/clang-8-macosx64.json
@@ -1,29 +1,28 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "osx_cross_compile": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_701/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_701/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/clang/bin/clang",
     "cxx": "/builds/worker/workspace/build/src/clang/bin/clang++",
     "as": "/builds/worker/workspace/build/src/clang/bin/clang",
     "ar": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-ar",
     "ranlib": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-ranlib",
     "libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-libtool",
     "ld": "/builds/worker/workspace/build/src/clang/bin/clang",
     "patches": [
       "static-llvm-symbolizer.patch",
       "compiler-rt-cross-compile.patch",
-      "compiler-rt-no-codesign.patch",
-      "r350774.patch"
+      "compiler-rt-no-codesign.patch"
     ]
 }
rename from build/build-clang/clang-trunk-mingw.json
rename to build/build-clang/clang-8-mingw.json
--- a/build/build-clang/clang-trunk-mingw.json
+++ b/build/build-clang/clang-8-mingw.json
@@ -11,11 +11,10 @@
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "patches": [
-      "r350774.patch"
     ]
 }
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-tidy-8.patch
@@ -0,0 +1,10 @@
+--- a/extra/clang-tidy/tool/run-clang-tidy.py	2019-03-27 15:12:48.000000000 +0200
++++ b/extra/clang-tidy/tool/run-clang-tidy.py	2019-03-27 15:12:39.000000000 +0200
+@@ -169,6 +169,7 @@
+     with lock:
+       sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8') + '\n')
+       if len(err) > 0:
++        sys.stdout.flush()
+         sys.stderr.write(err.decode('utf-8') + '\n')
+     queue.task_done()
+ 
--- a/build/build-clang/clang-tidy-linux64.json
+++ b/build/build-clang/clang-tidy-linux64.json
@@ -1,20 +1,21 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final/",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final/",
-    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_701/final/",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final/",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_701/final/",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final/",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final/",
+    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_800/final/",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final/",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final/",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "patches": [
+      "clang-tidy-8.patch"
     ]
 }
--- a/build/build-clang/clang-tidy-macosx64.json
+++ b/build/build-clang/clang-tidy-macosx64.json
@@ -1,25 +1,26 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
     "osx_cross_compile": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_701/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
+    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_800/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_800/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/clang/bin/clang",
     "cxx": "/builds/worker/workspace/build/src/clang/bin/clang++",
     "as": "/builds/worker/workspace/build/src/clang/bin/clang",
     "ar": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-ar",
     "ranlib": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-ranlib",
     "libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-darwin11-libtool",
     "ld": "/builds/worker/workspace/build/src/clang/bin/clang",
     "patches": [
+      "clang-tidy-8.patch"
     ]
 }
--- a/build/build-clang/clang-tidy-win64.json
+++ b/build/build-clang/clang-tidy-win64.json
@@ -1,19 +1,20 @@
 {
-    "llvm_revision": "349247",
+    "llvm_revision": "356365",
     "stages": "1",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_701/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/final",
+    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_800/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/final",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "ml": "ml64.exe",
     "patches": [
+      "clang-tidy-8.patch"
     ]
 }
deleted file mode 100644
--- a/build/build-clang/r350774.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp
-index d7c25921ec3..fe41987f5c2 100644
---- a/llvm/lib/Object/Binary.cpp
-+++ b/llvm/lib/Object/Binary.cpp
-@@ -88,7 +88,8 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
- 
- Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
-   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
--      MemoryBuffer::getFileOrSTDIN(Path);
-+      MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
-+                                   /*RequiresNullTerminator=*/false);
-   if (std::error_code EC = FileOrErr.getError())
-     return errorCodeToError(EC);
-   std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
--- a/build/build-clang/rename_gcov_flush_.patch
+++ b/build/build-clang/rename_gcov_flush_.patch
@@ -7,8 +7,21 @@ diff --git a/compiler-rt/lib/profile/GCD
    fn_list_insert(&flush_fn_list, fn);
  }
  
 -void __gcov_flush() {
 +void __custom_llvm_gcov_flush() {
    struct fn_node* curr = flush_fn_list.head;
  
    while (curr) {
+diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+index 9af64ed332c..bcebe303ff4 100644
+--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
++++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+@@ -648,7 +648,7 @@ void GCOVProfiler::AddFlushBeforeForkAndExec() {
+   for (auto I : ForkAndExecs) {
+     IRBuilder<> Builder(I);
+     FunctionType *FTy = FunctionType::get(Builder.getVoidTy(), {}, false);
+-    Constant *GCOVFlush = M->getOrInsertFunction("__gcov_flush", FTy);
++    Constant *GCOVFlush = M->getOrInsertFunction("__custom_llvm_gcov_flush", FTy);
+     Builder.CreateCall(GCOVFlush);
+     I->getParent()->splitBasicBlock(I);
+   }
--- a/build/clang-plugin/import_mozilla_checks.py
+++ b/build/clang-plugin/import_mozilla_checks.py
@@ -49,16 +49,17 @@ add_clang_library(clangTidyMozillaModule
   LINK_LIBS
   clangAST
   clangASTMatchers
   clangBasic
   clangLex
   clangTidy
   clangTidyReadabilityModule
   clangTidyUtils
+  clangTidyMPIModule
   )""" % {'names': "\n".join(names)})
 
 
 def add_item_to_cmake_section(cmake_path, section, library):
     with open(cmake_path, 'r') as f:
         lines = f.readlines()
     f.close()
 
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -65,16 +65,21 @@ third_party_paths.inputs = [
 
 HOST_COMPILE_FLAGS['STL'] = []
 HOST_COMPILE_FLAGS['VISIBILITY'] = []
 
 # libc++ is required to build plugins against clang on OS X.
 if CONFIG['HOST_OS_ARCH'] == 'Darwin':
     HOST_CXXFLAGS += ['-stdlib=libc++']
 
+# As of clang 8, llvm-config doesn't output the flags used to build clang
+# itself, so we don't end up with -fPIC as a side effect. llvm.org/PR8220
+if CONFIG['HOST_OS_ARCH'] != 'Windows':
+    HOST_CXXFLAGS += ['-fPIC']
+
 DIRS += [
     'tests',
 ]
 
 
 # In the current moz.build world, we need to override essentially every
 # variable to limit ourselves to what we need to build the clang plugin.
 if CONFIG['HOST_OS_ARCH'] == 'WINNT':
--- a/build/moz.build
+++ b/build/moz.build
@@ -109,16 +109,17 @@ if CONFIG['ENABLE_TESTS']:
 # NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir.
 OBJDIR_FILES += ['/.gdbinit']
 
 # Put a .lldbinit in the bin directory and the objdir, to be picked up
 # automatically by LLDB when we debug executables using either of those two
 # directories as the current working directory.  The .lldbinit file will
 # load $(topsrcdir)/.lldbinit, which is where the actual debugging commands are.
 DEFINES['topsrcdir'] = TOPSRCDIR
+DEFINES['topobjdir'] = TOPOBJDIR
 FINAL_TARGET_PP_FILES += ['.lldbinit.in']
 OBJDIR_FILES += ['!/dist/bin/.lldbinit']
 
 # Put the .ycm_extra_conf.py file at the root of the objdir. It is used by
 # the vim plugin YouCompleteMe.
 OBJDIR_FILES += ['/.ycm_extra_conf.py']
 
 if CONFIG['MOZ_VALGRIND']:
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1143,17 +1143,18 @@ nsresult nsScriptSecurityManager::Report
   nsCOMPtr<nsIConsoleService> console(
       do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
   nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
   NS_ENSURE_TRUE(error, NS_ERROR_FAILURE);
 
   // using category of "SOP" so we can link to MDN
   rv = error->Init(message, EmptyString(), EmptyString(), 0, 0,
-                   nsIScriptError::errorFlag, "SOP", aFromPrivateWindow);
+                   nsIScriptError::errorFlag, "SOP", aFromPrivateWindow,
+                   true /* From chrome context */);
   NS_ENSURE_SUCCESS(rv, rv);
   console->LogMessage(error);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(
     nsIPrincipal* aPrincipal, const nsACString& aTargetURIStr,
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -75,20 +75,20 @@ void nsChromeRegistry::LogMessageWithCon
   va_start(args, aMsg);
   mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args);
   va_end(args);
   if (!formatted) return;
 
   nsCString spec;
   if (aURL) aURL->GetSpec(spec);
 
-  rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()),
-                   NS_ConvertUTF8toUTF16(spec), EmptyString(), aLineNumber, 0,
-                   flags, "chrome registration",
-                   false /* from private window */);
+  rv = error->Init(
+      NS_ConvertUTF8toUTF16(formatted.get()), NS_ConvertUTF8toUTF16(spec),
+      EmptyString(), aLineNumber, 0, flags, "chrome registration",
+      false /* from private window */, true /* from chrome context */);
 
   if (NS_FAILED(rv)) return;
 
   console->LogMessage(error);
 }
 
 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry = nullptr; }
 
--- a/devtools/client/webconsole/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/consoleApi.js
@@ -2099,16 +2099,17 @@ stubPreparedMessages.set(`console.countR
 
 stubPackets.set(`console.log('foobar', 'test')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "foobar",
       "test"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2128,16 +2129,17 @@ stubPackets.set(`console.log('foobar', '
 stubPackets.set(`console.log(undefined)`, {
   "message": {
     "addonId": "",
     "arguments": [
       {
         "type": "undefined"
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2155,16 +2157,17 @@ stubPackets.set(`console.log(undefined)`
 });
 
 stubPackets.set(`console.warn('danger, will robinson!')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "danger, will robinson!"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "warn",
     "lineNumber": 1,
     "prefix": "",
@@ -2184,16 +2187,17 @@ stubPackets.set(`console.warn('danger, w
 stubPackets.set(`console.log(NaN)`, {
   "message": {
     "addonId": "",
     "arguments": [
       {
         "type": "NaN"
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2213,16 +2217,17 @@ stubPackets.set(`console.log(NaN)`, {
 stubPackets.set(`console.log(null)`, {
   "message": {
     "addonId": "",
     "arguments": [
       {
         "type": "null"
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2240,16 +2245,17 @@ stubPackets.set(`console.log(null)`, {
 });
 
 stubPackets.set(`console.log('鼬')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "鼬"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2265,16 +2271,17 @@ stubPackets.set(`console.log('鼬')`, {
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.clear()`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "clear",
     "lineNumber": 1,
     "prefix": "",
@@ -2292,16 +2299,17 @@ stubPackets.set(`console.clear()`, {
 });
 
 stubPackets.set(`console.count('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": {
       "count": 1,
       "label": "bar"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -2345,16 +2353,17 @@ stubPackets.set(`console.assert(false, {
           },
           "ownSymbols": [],
           "ownPropertiesLength": 1,
           "ownSymbolsLength": 0,
           "safeGetterValues": {}
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "assert",
     "lineNumber": 1,
     "prefix": "",
@@ -2381,16 +2390,17 @@ stubPackets.set(`console.assert(false, {
 });
 
 stubPackets.set(`console.log('hello \nfrom \rthe \"string world!')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "hello \nfrom \rthe \"string world!"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2408,16 +2418,17 @@ stubPackets.set(`console.log('hello \nfr
 });
 
 stubPackets.set(`console.log('úṇĩçödê țĕșť')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "úṇĩçödê țĕșť"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2447,16 +2458,17 @@ stubPackets.set(`console.dirxml(window)`
         "sealed": false,
         "ownPropertyLength": 830,
         "preview": {
           "kind": "ObjectWithURL",
           "url": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html"
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "dirxml",
     "lineNumber": 1,
     "prefix": "",
@@ -2492,16 +2504,17 @@ stubPackets.set(`console.log('myarray', 
           "items": [
             "red",
             "green",
             "blue"
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2529,16 +2542,17 @@ stubPackets.set(`console.log('myregex', 
         "class": "RegExp",
         "extensible": true,
         "frozen": false,
         "sealed": false,
         "ownPropertyLength": 1,
         "displayString": "/a.b.c/"
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2573,16 +2587,17 @@ stubPackets.set(`console.table(['red', '
           "items": [
             "red",
             "green",
             "blue"
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "table",
     "lineNumber": 1,
     "prefix": "",
@@ -2636,16 +2651,17 @@ stubPackets.set(`console.log('myobject',
           },
           "ownSymbols": [],
           "ownPropertiesLength": 3,
           "ownSymbolsLength": 0,
           "safeGetterValues": {}
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "prefix": "",
@@ -2663,16 +2679,17 @@ stubPackets.set(`console.log('myobject',
 });
 
 stubPackets.set(`console.debug('debug message');`, {
   "message": {
     "addonId": "",
     "arguments": [
       "debug message"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "debug",
     "lineNumber": 1,
     "prefix": "",
@@ -2690,16 +2707,17 @@ stubPackets.set(`console.debug('debug me
 });
 
 stubPackets.set(`console.info('info message');`, {
   "message": {
     "addonId": "",
     "arguments": [
       "info message"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "info",
     "lineNumber": 1,
     "prefix": "",
@@ -2717,16 +2735,17 @@ stubPackets.set(`console.info('info mess
 });
 
 stubPackets.set(`console.error('error message');`, {
   "message": {
     "addonId": "",
     "arguments": [
       "error message"
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "error",
     "lineNumber": 1,
     "prefix": "",
@@ -2776,16 +2795,17 @@ stubPackets.set(`console.log('mymap')`, 
             [
               "key2",
               "value2"
             ]
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 5,
     "prefix": "",
@@ -2820,16 +2840,17 @@ stubPackets.set(`console.log('myset')`, 
           "length": 2,
           "items": [
             "a",
             "b"
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 2,
     "prefix": "",
@@ -2845,16 +2866,17 @@ stubPackets.set(`console.log('myset')`, 
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.trace()`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 11,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "testStacktraceFiltering",
     "groupName": "",
     "level": "trace",
     "lineNumber": 3,
     "prefix": "",
@@ -2937,16 +2959,17 @@ stubPackets.set(`console.trace('bar', {'
           "items": [
             1,
             2,
             3
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 11,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "testStacktraceWithLog",
     "groupName": "",
     "level": "trace",
     "lineNumber": 3,
     "prefix": "",
@@ -2987,16 +3010,17 @@ stubPackets.set(`console.trace('bar', {'
 });
 
 stubPackets.set(`console.time('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "time",
     "lineNumber": 2,
     "prefix": "",
@@ -3016,16 +3040,17 @@ stubPackets.set(`console.time('bar')`, {
 });
 
 stubPackets.set(`timerAlreadyExists`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "time",
     "lineNumber": 3,
     "prefix": "",
@@ -3046,16 +3071,17 @@ stubPackets.set(`timerAlreadyExists`, {
 });
 
 stubPackets.set(`console.timeLog('bar') - 1`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeLog",
     "lineNumber": 4,
     "prefix": "",
@@ -3101,16 +3127,17 @@ stubPackets.set(`console.timeLog('bar') 
           },
           "ownSymbols": [],
           "ownPropertiesLength": 1,
           "ownSymbolsLength": 0,
           "safeGetterValues": {}
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeLog",
     "lineNumber": 5,
     "prefix": "",
@@ -3131,16 +3158,17 @@ stubPackets.set(`console.timeLog('bar') 
 });
 
 stubPackets.set(`console.timeEnd('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeEnd",
     "lineNumber": 6,
     "prefix": "",
@@ -3161,16 +3189,17 @@ stubPackets.set(`console.timeEnd('bar')`
 });
 
 stubPackets.set(`timeEnd.timerDoesntExist`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeEnd",
     "lineNumber": 7,
     "prefix": "",
@@ -3191,16 +3220,17 @@ stubPackets.set(`timeEnd.timerDoesntExis
 });
 
 stubPackets.set(`timeLog.timerDoesntExist`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeLog",
     "lineNumber": 8,
     "prefix": "",
@@ -3221,16 +3251,17 @@ stubPackets.set(`timeLog.timerDoesntExis
 });
 
 stubPackets.set(`console.table('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "table",
     "lineNumber": 2,
     "prefix": "",
@@ -3265,16 +3296,17 @@ stubPackets.set(`console.table(['a', 'b'
           "items": [
             "a",
             "b",
             "c"
           ]
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "table",
     "lineNumber": 2,
     "prefix": "",
@@ -3292,16 +3324,17 @@ stubPackets.set(`console.table(['a', 'b'
 });
 
 stubPackets.set(`console.group('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
     "level": "group",
     "lineNumber": 2,
     "prefix": "",
@@ -3317,16 +3350,17 @@ stubPackets.set(`console.group('bar')`, 
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd('bar')`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
     "level": "groupEnd",
     "lineNumber": 3,
     "prefix": "",
@@ -3344,16 +3378,17 @@ stubPackets.set(`console.groupEnd('bar')
 });
 
 stubPackets.set(`console.groupCollapsed('foo')`, {
   "message": {
     "addonId": "",
     "arguments": [
       "foo"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
     "level": "groupCollapsed",
     "lineNumber": 2,
     "prefix": "",
@@ -3369,16 +3404,17 @@ stubPackets.set(`console.groupCollapsed(
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd('foo')`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
     "level": "groupEnd",
     "lineNumber": 3,
     "prefix": "",
@@ -3394,16 +3430,17 @@ stubPackets.set(`console.groupEnd('foo')
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.group()`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "group",
     "lineNumber": 2,
     "prefix": "",
@@ -3419,16 +3456,17 @@ stubPackets.set(`console.group()`, {
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd()`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "groupEnd",
     "lineNumber": 3,
     "prefix": "",
@@ -3447,16 +3485,17 @@ stubPackets.set(`console.groupEnd()`, {
 
 stubPackets.set(`console.log(%cfoobar)`, {
   "message": {
     "addonId": "",
     "arguments": [
       "foo",
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 2,
     "prefix": "",
@@ -3479,16 +3518,17 @@ stubPackets.set(`console.log(%cfoobar)`,
 stubPackets.set(`console.log("%cHello%c|%cWorld")`, {
   "message": {
     "addonId": "",
     "arguments": [
       "Hello",
       "|",
       "World"
     ],
+    "chromeContext": false,
     "columnNumber": 11,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 2,
     "prefix": "",
@@ -3511,16 +3551,17 @@ stubPackets.set(`console.log("%cHello%c|
 
 stubPackets.set(`console.group(%cfoo%cbar)`, {
   "message": {
     "addonId": "",
     "arguments": [
       "foo",
       "bar"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo bar",
     "level": "group",
     "lineNumber": 2,
     "prefix": "",
@@ -3539,16 +3580,17 @@ stubPackets.set(`console.group(%cfoo%cba
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd(%cfoo%cbar)`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo bar",
     "level": "groupEnd",
     "lineNumber": 6,
     "prefix": "",
@@ -3567,16 +3609,17 @@ stubPackets.set(`console.groupEnd(%cfoo%
 
 stubPackets.set(`console.groupCollapsed(%cfoo%cbaz)`, {
   "message": {
     "addonId": "",
     "arguments": [
       "foo",
       "baz"
     ],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo baz",
     "level": "groupCollapsed",
     "lineNumber": 2,
     "prefix": "",
@@ -3595,16 +3638,17 @@ stubPackets.set(`console.groupCollapsed(
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd(%cfoo%cbaz)`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 9,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo baz",
     "level": "groupEnd",
     "lineNumber": 6,
     "prefix": "",
@@ -3663,16 +3707,17 @@ stubPackets.set(`console.dir({C, M, Y, K
           },
           "ownSymbols": [],
           "ownPropertiesLength": 4,
           "ownSymbolsLength": 0,
           "safeGetterValues": {}
         }
       }
     ],
+    "chromeContext": false,
     "columnNumber": 35,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "dir",
     "lineNumber": 1,
     "prefix": "",
@@ -3690,16 +3735,17 @@ stubPackets.set(`console.dir({C, M, Y, K
 });
 
 stubPackets.set(`console.count | default: 1`, {
   "message": {
     "addonId": "",
     "arguments": [
       "default"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 1,
       "label": "default"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3720,16 +3766,17 @@ stubPackets.set(`console.count | default
 });
 
 stubPackets.set(`console.count | default: 2`, {
   "message": {
     "addonId": "",
     "arguments": [
       "default"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 2,
       "label": "default"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3750,16 +3797,17 @@ stubPackets.set(`console.count | default
 });
 
 stubPackets.set(`console.count | test counter: 1`, {
   "message": {
     "addonId": "",
     "arguments": [
       "test counter"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 1,
       "label": "test counter"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3780,16 +3828,17 @@ stubPackets.set(`console.count | test co
 });
 
 stubPackets.set(`console.count | test counter: 2`, {
   "message": {
     "addonId": "",
     "arguments": [
       "test counter"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 2,
       "label": "test counter"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3810,16 +3859,17 @@ stubPackets.set(`console.count | test co
 });
 
 stubPackets.set(`console.count | default: 3`, {
   "message": {
     "addonId": "",
     "arguments": [
       "default"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 3,
       "label": "default"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3838,16 +3888,17 @@ stubPackets.set(`console.count | default
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | clear`, {
   "message": {
     "addonId": "",
     "arguments": [],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "clear",
     "lineNumber": 7,
     "prefix": "",
@@ -3865,16 +3916,17 @@ stubPackets.set(`console.count | clear`,
 });
 
 stubPackets.set(`console.count | default: 4`, {
   "message": {
     "addonId": "",
     "arguments": [
       "default"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 4,
       "label": "default"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3895,16 +3947,17 @@ stubPackets.set(`console.count | default
 });
 
 stubPackets.set(`console.count | test counter: 3`, {
   "message": {
     "addonId": "",
     "arguments": [
       "test counter"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 3,
       "label": "test counter"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3925,16 +3978,17 @@ stubPackets.set(`console.count | test co
 });
 
 stubPackets.set(`console.countReset | test counter: 0`, {
   "message": {
     "addonId": "",
     "arguments": [
       "test counter"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "count": 0,
       "label": "test counter"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -3955,16 +4009,17 @@ stubPackets.set(`console.countReset | te
 });
 
 stubPackets.set(`console.countReset | counterDoesntExist`, {
   "message": {
     "addonId": "",
     "arguments": [
       "test counter"
     ],
+    "chromeContext": false,
     "columnNumber": 13,
     "counter": {
       "error": "counterDoesntExist",
       "label": "test counter"
     },
     "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -14,17 +14,16 @@
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/UnionConversions.h"
 #include "mozilla/EventDispatcher.h"
-#include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsGlobalWindow.h"
 #include "nsIConsoleService.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsNetUtil.h"
 #include "nsPresContext.h"
@@ -132,19 +131,20 @@ PostMessageEvent::Run() {
         rv = errorObject->InitWithSourceURI(
             errorText, callerDocumentURI, EmptyString(), 0, 0,
             nsIScriptError::errorFlag, "DOM Window", mCallerWindowID.value());
       } else {
         nsString uriSpec;
         rv = NS_GetSanitizedURIStringFromURI(callerDocumentURI, uriSpec);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        rv = errorObject->Init(errorText, uriSpec, EmptyString(), 0, 0,
-                               nsIScriptError::errorFlag, "DOM Window",
-                               mIsFromPrivateWindow);
+        rv = errorObject->Init(
+            errorText, uriSpec, EmptyString(), 0, 0, nsIScriptError::errorFlag,
+            "DOM Window", mIsFromPrivateWindow,
+            nsContentUtils::IsSystemPrincipal(mProvidedPrincipal));
       }
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIConsoleService> consoleService =
           do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       return consoleService->LogMessage(errorObject);
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_PostMessageEvent_h
 
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "nsCOMPtr.h"
 #include "mozilla/MaybeOneOf.h"
 #include "mozilla/RefPtr.h"
+#include "nsContentUtils.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 
 class nsGlobalWindowOuter;
 class nsGlobalWindowInner;
 class nsIPrincipal;
 
 namespace mozilla {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3729,26 +3729,27 @@ nsresult nsContentUtils::FormatLocalized
   }
   return FormatLocalizedString(aFile, aKey, params.get(), paramsLength,
                                aResult);
 }
 
 /* static */
 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
                                            const char* classification,
-                                           bool aFromPrivateWindow) {
+                                           bool aFromPrivateWindow,
+                                           bool aFromChromeContext) {
   nsCOMPtr<nsIScriptError> scriptError =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   if (scriptError) {
     nsCOMPtr<nsIConsoleService> console =
         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-    if (console &&
-        NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(), EmptyString(),
-                                       0, 0, nsIScriptError::errorFlag,
-                                       classification, aFromPrivateWindow))) {
+    if (console && NS_SUCCEEDED(scriptError->Init(
+                       aErrorText, EmptyString(), EmptyString(), 0, 0,
+                       nsIScriptError::errorFlag, classification,
+                       aFromPrivateWindow, aFromChromeContext))) {
       console->LogMessage(scriptError);
     }
   }
 }
 
 /* static */
 nsresult nsContentUtils::ReportToConsole(
     uint32_t aErrorFlags, const nsACString& aCategory,
@@ -5294,30 +5295,32 @@ nsContentUtils::GetMostRecentNonPBWindow
 
   return pwindow.forget();
 }
 
 /* static */
 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
   nsAutoString msg;
   bool privateBrowsing = false;
+  bool chromeContext = false;
 
   if (aDocument) {
     nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
     if (uri) {
       msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
       msg.AppendLiteral(" : ");
     }
     privateBrowsing =
         !!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
+    chromeContext = IsSystemPrincipal(aDocument->NodePrincipal());
   }
 
   msg.AppendLiteral(
       "Unable to run script because scripts are blocked internally.");
-  LogSimpleConsoleError(msg, "DOM", privateBrowsing);
+  LogSimpleConsoleError(msg, "DOM", privateBrowsing, chromeContext);
 }
 
 /* static */
 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
   nsCOMPtr<nsIRunnable> runnable = aRunnable;
   if (!runnable) {
     return;
   }
@@ -6508,17 +6511,17 @@ bool nsContentUtils::IsPatternMatching(n
 
   // The pattern has to match the entire value.
   aPattern.InsertLiteral(u"^(?:", 0);
   aPattern.AppendLiteral(")$");
 
   JS::Rooted<JSObject*> re(
       cx,
       JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
-                            aPattern.Length(), JS::RegExpFlags::Unicode));
+                            aPattern.Length(), JS::RegExpFlag::Unicode));
   if (!re) {
     // Remove extra patterns added above to report with the original pattern.
     aPattern.Cut(0, 4);
     aPattern.Cut(aPattern.Length() - 2, 2);
     ReportPatternCompileFailure(aPattern, aDocument, cx);
     return true;
   }
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1020,17 +1020,18 @@ class nsContentUtils {
 
   /**
    * Report simple error message to the browser console
    *   @param aErrorText the error message
    *   @param classification Name of the module reporting error
    */
   static void LogSimpleConsoleError(const nsAString& aErrorText,
                                     const char* classification,
-                                    bool aFromPrivateWindow);
+                                    bool aFromPrivateWindow,
+                                    bool aFromChromeContext);
 
   /**
    * Report a non-localized error message to the error console.
    *   @param aErrorText the error message
    *   @param aErrorFlags See nsIScriptError.
    *   @param aCategory Name of module reporting error.
    *   @param aDocument Reference to the document which triggered the message.
    *   @param [aURI=nullptr] (Optional) URI of resource containing error.
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -404,17 +404,18 @@ bool nsFrameMessageManager::GetParamsFor
     uint32_t lineno = 0, column = 0;
     nsJSUtils::GetCallingLocation(aCx, filename, &lineno, &column);
     nsCOMPtr<nsIScriptError> error(
         do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
     error->Init(NS_LITERAL_STRING("Sending message that cannot be cloned. Are "
                                   "you trying to send an XPCOM object?"),
                 filename, EmptyString(), lineno, column,
                 nsIScriptError::warningFlag, "chrome javascript",
-                false /* from private window */);
+                false /* from private window */,
+                true /* from chrome context */);
     console->LogMessage(error);
   }
 
   // Not clonable, try JSON
   // XXX This is ugly but currently structured cloning doesn't handle
   //    properly cases when interface is implemented in JS and used
   //    as a dictionary.
   nsAutoString json;
@@ -819,17 +820,18 @@ void nsFrameMessageManager::ReceiveMessa
 
           nsCOMPtr<nsIConsoleService> console(
               do_GetService(NS_CONSOLESERVICE_CONTRACTID));
           if (console) {
             nsCOMPtr<nsIScriptError> error(
                 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
             error->Init(msg, EmptyString(), EmptyString(), 0, 0,
                         nsIScriptError::warningFlag, "chrome javascript",
-                        false /* from private window */);
+                        false /* from private window */,
+                        true /* from chrome context */);
             console->LogMessage(error);
           }
 
           JS_ClearPendingException(cx);
           continue;
         }
       }
     }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5946,17 +5946,18 @@ bool nsGlobalWindowOuter::GetPrincipalFo
         }
 
         nsContentUtils::LogSimpleConsoleError(
             NS_ConvertUTF8toUTF16(nsPrintfCString(
                 R"(Attempting to post a message to window with url "%s" and )"
                 R"(origin "%s" from a system principal scope with mismatched )"
                 R"(origin "%s".)",
                 targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
-            "DOM", !!principal->PrivateBrowsingId());
+            "DOM", !!principal->PrivateBrowsingId(),
+            nsContentUtils::IsSystemPrincipal(principal));
 
         attrs = principal->OriginAttributesRef();
       }
     }
 
     // Create a nsIPrincipal inheriting the app/browser attributes from the
     // caller.
     providedPrincipal =
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -753,17 +753,17 @@ tags = audiochannel
 [test_NodeIterator_mutations_3.html]
 [test_nodelist_holes.html]
 [test_noWebAudioNotification.html]
 tags = audiochannel
 [test_open_null_features.html]
 [test_openDialogChromeOnly.html]
 tags = openwindow
 [test_plugin_freezing.html]
-skip-if = toolkit == 'android' #CLICK_TO_PLAY
+skip-if = toolkit == 'android' || (os == 'win' && processor == 'aarch64') #CLICK_TO_PLAY, aarch64 due to 1538785
 [test_pluginAudioNotification.html]
 tags = audiochannel
 skip-if = toolkit == 'android' # Plugins don't work on Android
 [test_pluginMutedBeforePlay.html]
 tags = audiochannel
 skip-if = toolkit == 'android' # Plugins don't work on Android
 [test_postMessage_solidus.html]
 [test_postMessages.html]
--- a/dom/bindings/nsIScriptError.idl
+++ b/dom/bindings/nsIScriptError.idl
@@ -88,16 +88,18 @@ interface nsIScriptError : nsIConsoleMes
     readonly attribute unsigned long long outerWindowID;
 
     /* Get the inner window id this was initialized with.  Zero will be
        returned if init() was used instead of initWithWindowID(). */
     readonly attribute unsigned long long innerWindowID;
 
     readonly attribute boolean isFromPrivateWindow;
 
+    readonly attribute boolean isFromChromeContext;
+
     attribute jsval stack;
 
     /**
      * If |stack| is an object, then stackGlobal must be a global object that's
      * same-compartment with |stack|. This can be used to enter the correct
      * realm when working with the stack object. We can't use the object itself
      * because it might be a cross-compartment wrapper and CCWs are not
      * associated with a single realm/global.
@@ -126,17 +128,18 @@ interface nsIScriptError : nsIConsoleMes
 
     void init(in AString message,
               in AString sourceName,
               in AString sourceLine,
               in uint32_t lineNumber,
               in uint32_t columnNumber,
               in uint32_t flags,
               in string category,
-              [optional] in bool fromPrivateWindow);
+              [optional] in bool fromPrivateWindow,
+              [optional] in bool fromChromeContext);
 
     /* This should be called instead of nsIScriptError.init to
      * initialize with a window id.  The window id should be for the
      * inner window associated with this error.
      *
      * This function will check whether sourceName is a uri and sanitize it if
      * needed. If you know the source name is sanitized already, use
      * initWithSanitizedSource.
--- a/dom/bindings/nsScriptError.cpp
+++ b/dom/bindings/nsScriptError.cpp
@@ -40,17 +40,18 @@ nsScriptErrorBase::nsScriptErrorBase()
       mColumnNumber(0),
       mFlags(0),
       mCategory(),
       mOuterWindowID(0),
       mInnerWindowID(0),
       mTimeStamp(0),
       mTimeWarpTarget(0),
       mInitializedOnMainThread(false),
-      mIsFromPrivateWindow(false) {}
+      mIsFromPrivateWindow(false),
+      mIsFromChromeContext(false) {}
 
 nsScriptErrorBase::~nsScriptErrorBase() {}
 
 void nsScriptErrorBase::AddNote(nsIScriptErrorNote* note) {
   mNotes.AppendObject(note);
 }
 
 void nsScriptErrorBase::InitializeOnMainThread() {
@@ -58,17 +59,17 @@ void nsScriptErrorBase::InitializeOnMain
   MOZ_ASSERT(!mInitializedOnMainThread);
 
   if (mInnerWindowID) {
     nsGlobalWindowInner* window =
         nsGlobalWindowInner::GetInnerWindowWithId(mInnerWindowID);
     if (window) {
       nsPIDOMWindowOuter* outer = window->GetOuterWindow();
       if (outer) mOuterWindowID = outer->WindowID();
-
+      mIsFromChromeContext = ComputeIsFromChromeContext(window);
       mIsFromPrivateWindow = ComputeIsFromPrivateWindow(window);
     }
   }
 
   mInitializedOnMainThread = true;
 }
 
 NS_IMETHODIMP
@@ -212,23 +213,25 @@ static void AssignSourceNameHelper(nsIUR
     aSourceNameDest.AssignLiteral("[nsIURI::GetSpec failed]");
   }
 }
 
 NS_IMETHODIMP
 nsScriptErrorBase::Init(const nsAString& message, const nsAString& sourceName,
                         const nsAString& sourceLine, uint32_t lineNumber,
                         uint32_t columnNumber, uint32_t flags,
-                        const char* category, bool fromPrivateWindow) {
+                        const char* category, bool fromPrivateWindow,
+                        bool fromChromeContext) {
   InitializationHelper(message, sourceLine, lineNumber, columnNumber, flags,
                        category ? nsDependentCString(category) : EmptyCString(),
                        0 /* inner Window ID */);
   AssignSourceNameHelper(mSourceName, sourceName);
 
   mIsFromPrivateWindow = fromPrivateWindow;
+  mIsFromChromeContext = fromChromeContext;
   return NS_OK;
 }
 
 void nsScriptErrorBase::InitializationHelper(
     const nsAString& message, const nsAString& sourceLine, uint32_t lineNumber,
     uint32_t columnNumber, uint32_t flags, const nsACString& category,
     uint64_t aInnerWindowID) {
   mMessage.Assign(message);
@@ -389,16 +392,28 @@ nsScriptErrorBase::SetTimeWarpTarget(uin
 
 NS_IMETHODIMP
 nsScriptErrorBase::GetTimeWarpTarget(uint64_t* aTarget) {
   *aTarget = mTimeWarpTarget;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptErrorBase::GetIsFromChromeContext(bool* aIsFromChromeContext) {
+  NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
+                       "This can't be safely determined off the main thread, "
+                       "returning an inaccurate value!");
+  if (!mInitializedOnMainThread && NS_IsMainThread()) {
+    InitializeOnMainThread();
+  }
+  *aIsFromChromeContext = mIsFromChromeContext;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScriptErrorBase::GetNotes(nsIArray** aNotes) {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t len = mNotes.Length();
   for (uint32_t i = 0; i < len; i++) array->AppendElement(mNotes[i]);
   array.forget(aNotes);
@@ -411,16 +426,23 @@ bool nsScriptErrorBase::ComputeIsFromPri
     nsGlobalWindowInner* aWindow) {
   // Never mark exceptions from chrome windows as having come from private
   // windows, since we always want them to be reported.
   nsIPrincipal* winPrincipal = aWindow->GetPrincipal();
   return aWindow->IsPrivateBrowsing() &&
          !nsContentUtils::IsSystemPrincipal(winPrincipal);
 }
 
+/* static */
+bool nsScriptErrorBase::ComputeIsFromChromeContext(
+    nsGlobalWindowInner* aWindow) {
+  nsIPrincipal* winPrincipal = aWindow->GetPrincipal();
+  return nsContentUtils::IsSystemPrincipal(winPrincipal);
+}
+
 NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError)
 
 nsScriptErrorNote::nsScriptErrorNote()
     : mMessage(),
       mSourceName(),
       mSourceId(0),
       mLineNumber(0),
       mColumnNumber(0) {}
--- a/dom/bindings/nsScriptError.h
+++ b/dom/bindings/nsScriptError.h
@@ -50,16 +50,18 @@ class nsScriptErrorBase : public nsIScri
 
   NS_DECL_NSICONSOLEMESSAGE
   NS_DECL_NSISCRIPTERROR
 
   void AddNote(nsIScriptErrorNote* note);
 
   static bool ComputeIsFromPrivateWindow(nsGlobalWindowInner* aWindow);
 
+  static bool ComputeIsFromChromeContext(nsGlobalWindowInner* aWindow);
+
  protected:
   virtual ~nsScriptErrorBase();
 
   void InitializeOnMainThread();
 
   void InitializationHelper(const nsAString& message,
                             const nsAString& sourceLine, uint32_t lineNumber,
                             uint32_t columnNumber, uint32_t flags,
@@ -77,20 +79,21 @@ class nsScriptErrorBase : public nsIScri
   uint32_t mColumnNumber;
   uint32_t mFlags;
   nsCString mCategory;
   // mOuterWindowID is set on the main thread from InitializeOnMainThread().
   uint64_t mOuterWindowID;
   uint64_t mInnerWindowID;
   int64_t mTimeStamp;
   uint64_t mTimeWarpTarget;
-  // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
-  // thread from InitializeOnMainThread().
+  // mInitializedOnMainThread, mIsFromPrivateWindow and mIsFromChromeContext are
+  // set on the main thread from InitializeOnMainThread().
   mozilla::Atomic<bool> mInitializedOnMainThread;
   bool mIsFromPrivateWindow;
+  bool mIsFromChromeContext;
 };
 
 class nsScriptError final : public nsScriptErrorBase {
  public:
   nsScriptError() {}
   NS_DECL_THREADSAFE_ISUPPORTS
 
  private:
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3906,18 +3906,18 @@ gfxFontGroup* CanvasRenderingContext2D::
       gfxTextPerfMetrics* tp = nullptr;
       if (presShell && !presShell->IsDestroying()) {
         tp = presShell->GetPresContext()->GetTextPerfMetrics();
       }
       int32_t perDevPixel, perCSSPixel;
       GetAppUnitsValues(&perDevPixel, &perCSSPixel);
       gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
       CurrentState().fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(
-          FontFamilyList(eFamily_sans_serif), &style, tp, nullptr,
-          devToCssSize);
+          FontFamilyList(StyleGenericFontFamily::SansSerif), &style, tp,
+          nullptr, devToCssSize);
       if (CurrentState().fontGroup) {
         CurrentState().font = kDefaultFontStyle;
       } else {
         NS_ERROR("Default canvas font is invalid");
       }
     }
   }
 
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -1271,16 +1271,17 @@ void Console::ProfileMethodInternal(JSCo
 // static
 void Console::ProfileMethodMainthread(JSContext* aCx, const nsAString& aAction,
                                       const Sequence<JS::Value>& aData) {
   MOZ_ASSERT(NS_IsMainThread());
   ConsoleCommon::ClearException ce(aCx);
 
   RootedDictionary<ConsoleProfileEvent> event(aCx);
   event.mAction = aAction;
+  event.mChromeContext = nsContentUtils::ThreadsafeIsSystemCaller(aCx);
 
   event.mArguments.Construct();
   Sequence<JS::Value>& sequence = event.mArguments.Value();
 
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     if (!sequence.AppendElement(aData[i], fallible)) {
       return;
     }
@@ -1678,16 +1679,18 @@ bool Console::PopulateConsoleNotificatio
   ConsoleCommon::ClearException ce(aCx);
   RootedDictionary<ConsoleEvent> event(aCx);
 
   event.mAddonId = aData->mAddonId;
 
   event.mID.Construct();
   event.mInnerID.Construct();
 
+  event.mChromeContext = nsContentUtils::ThreadsafeIsSystemCaller(aCx);
+
   if (aData->mIDType == ConsoleCallData::eString) {
     event.mID.Value().SetAsString() = aData->mOuterIDString;
     event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
   } else if (aData->mIDType == ConsoleCallData::eNumber) {
     event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
     event.mInnerID.Value().SetAsUnsignedLongLong() = aData->mInnerIDNumber;
   } else {
     // aData->mIDType can be eUnknown when we dispatch notifications via
--- a/dom/console/ConsoleInstance.cpp
+++ b/dom/console/ConsoleInstance.cpp
@@ -34,28 +34,30 @@ ConsoleLogLevel PrefToValue(const nsAStr
   nsAutoCString value;
   nsresult rv = Preferences::GetCString(pref.get(), value);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     nsString message;
     message.AssignLiteral(
         "Console.maxLogLevelPref used with a non-existing pref: ");
     message.Append(aPref);
 
-    nsContentUtils::LogSimpleConsoleError(message, "chrome", false);
+    nsContentUtils::LogSimpleConsoleError(message, "chrome", false,
+                                          true /* from chrome context*/);
     return ConsoleLogLevel::All;
   }
 
   int index = FindEnumStringIndexImpl(value.get(), value.Length(),
                                       ConsoleLogLevelValues::strings);
   if (NS_WARN_IF(index < 0)) {
     nsString message;
     message.AssignLiteral("Invalid Console.maxLogLevelPref value: ");
     message.Append(NS_ConvertUTF8toUTF16(value));
 
-    nsContentUtils::LogSimpleConsoleError(message, "chrome", false);
+    nsContentUtils::LogSimpleConsoleError(message, "chrome", false,
+                                          true /* from chrome context*/);
     return ConsoleLogLevel::All;
   }
 
   MOZ_ASSERT(index < (int)ConsoleLogLevel::EndGuard_);
   return static_cast<ConsoleLogLevel>(index);
 }
 
 ConsoleUtils::Level WebIDLevelToConsoleUtilsLevel(ConsoleLevel aLevel) {
--- a/dom/console/tests/test_console_binding.html
+++ b/dom/console/tests/test_console_binding.html
@@ -13,16 +13,17 @@ function consoleListener() {
   SpecialPowers.addObserver(this, "console-api-log-event");
 }
 
 var order = 0;
 consoleListener.prototype  = {
   observe(aSubject, aTopic, aData) {
     if (aTopic == "console-api-log-event") {
       var obj = aSubject.wrappedJSObject;
+      ok(!obj.chromeContext, "Thils is not a chrome context");
       if (order + 1 == parseInt(obj.arguments[0])) {
         ok(true, "Message received: " + obj.arguments[0]);
         order++;
       }
 
       if (order == 3) {
         SpecialPowers.removeObserver(this, "console-api-log-event");
         SimpleTest.finish();
--- a/dom/console/tests/test_devtools_pref.html
+++ b/dom/console/tests/test_devtools_pref.html
@@ -10,16 +10,17 @@
   <script type="application/javascript">
 
 function consoleListener(expected) {
   var messages = [];
   return new Promise(done => {
     let observer = {
       observe: function listener(aSubject, aTopic, aData) {
         var obj = aSubject.wrappedJSObject;
+        ok(!obj.chromeContext, "This is not a chrome context");
         messages.push(parseInt(obj.arguments[0]));
         if (messages.length == expected) {
           SpecialPowers.removeObserver(observer, "console-api-log-event");
           SpecialPowers.removeObserver(observer, "console-api-profiler");
           done(messages);
         }
       },
     };
--- a/dom/console/tests/test_jsm.xul
+++ b/dom/console/tests/test_jsm.xul
@@ -29,16 +29,18 @@ function consoleListener() {
 }
 
 consoleListener.prototype  = {
   count: 0,
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "console-api-log-event") {
       var obj = aSubject.wrappedJSObject;
+      ok(obj.chromeContext, "JSM is always a chrome context");
+
       if (obj.innerID == JSM) {
         is(obj.ID, "jsm", "ID and InnerID are correctly set.");
         is(obj.arguments[0], "Hello world!", "Message matches");
         is(obj.consoleID, "", "No consoleID for console API");
         is(obj.prefix, "", "prefix is empty by default");
 
         // We want to see 2 messages from this innerID, the first is generated
         // by console.log, the second one from createInstance().log();
--- a/dom/events/test/test_slotted_mouse_event.html
+++ b/dom/events/test/test_slotted_mouse_event.html
@@ -1,61 +1,97 @@
 <!doctype html>
 <meta charset="utf-8">
 <title>Bug 1481500: mouse enter / leave events in slotted content</title>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <script src="/tests/SimpleTest/EventUtils.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <script>
+// We move the mouse from the #host to #target, then to #child-target.
+//
+// By the time we get to #child-target, we shouldn't have fired any mouseleave.
 function runTests() {
   let iframe = document.createElement('iframe');
+  iframe.style.width = "600px";
+  iframe.style.height = "600px";
   document.body.appendChild(iframe);
   iframe.onload = () => frameLoaded(iframe);
-  iframe.srcdoc =
-    `<div id="host"><div id="target"></div></div>`
+  iframe.srcdoc = `
+    <style>
+      #child-target {
+        width: 80px;
+        height: 80px;
+        background: yellow;
+      }
+    </style>
+    <div id="host"><div id="target"><div id="child-target"></div></div></div>
+  `;
 }
 
 function frameLoaded(iframe) {
   let host = iframe.contentDocument.getElementById('host');
   let target = iframe.contentDocument.getElementById('target');
+  let childTarget = iframe.contentDocument.getElementById('child-target');
+  let sawHost = false;
+  let sawTarget = false;
   let finished = false;
 
   host.attachShadow({ mode: 'open' }).innerHTML = `
     <style>
       :host {
-        width: 300px;
-        height: 300px;
+        width: 500px;
+        height: 500px;
         background: purple;
       }
       ::slotted(div) {
-        width: 100px;
-        height: 100px;
+        width: 200px;
+        height: 200px;
         background: green;
       }
     </style>
     <slot></slot>
   `;
 
+  host.addEventListener("mouseenter", e => {
+    if (finished)
+      return;
+    sawHost = true;
+    ok(true, "Should fire mouseenter on the host.");
+  });
+
   host.addEventListener("mouseleave", e => {
     if (finished)
       return;
     ok(false, "Should not fire mouseleave when moving the cursor to the slotted target");
   });
 
   target.addEventListener("mouseenter", () => {
     if (finished)
       return;
+    ok(sawHost, "Should've seen the hostmouseenter already");
+    sawTarget = true;
     ok(true, "Moving the mouse into the target should trigger a mouseenter there");
-    setTimeout(() => {
-      finished = true;
-      SimpleTest.finish()
-    }, 0);
+  });
+
+  target.addEventListener("mouseleave", () => {
+    if (finished)
+      return;
+    ok(false, "Should not fire mouseleave when moving the cursor to the slotted target's child");
+  });
+
+  childTarget.addEventListener("mouseenter", () => {
+    if (finished)
+      return;
+    ok(sawTarget, "Should've seen the target mouseenter already");
+    finished = true;
+    SimpleTest.finish();
   });
 
   synthesizeMouseAtCenter(host, { type: "mousemove" });
   synthesizeMouseAtCenter(target, { type: "mousemove" });
+  synthesizeMouseAtCenter(childTarget, { type: "mousemove" });
 }
 
 SimpleTest.waitForExplicitFinish();
 window.onload = () => {
   SimpleTest.waitForFocus(runTests);
 };
 </script>
--- a/dom/indexedDB/ReportInternalError.cpp
+++ b/dom/indexedDB/ReportInternalError.cpp
@@ -21,14 +21,15 @@ void ReportInternalError(const char* aFi
     if (*p == '/' && *(p + 1)) {
       aFile = p + 1;
     }
   }
 
   nsContentUtils::LogSimpleConsoleError(
       NS_ConvertUTF8toUTF16(
           nsPrintfCString("IndexedDB %s: %s:%" PRIu32, aStr, aFile, aLine)),
-      "indexedDB", false /* no IDB in private window */);
+      "indexedDB", false /* no IDB in private window */,
+      true /* Internal errors are chrome context only */);
 }
 
 }  // namespace indexedDB
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/indexedDB/ScriptErrorHelper.cpp
+++ b/dom/indexedDB/ScriptErrorHelper.cpp
@@ -106,17 +106,18 @@ class ScriptErrorRunnable final : public
           aMessage, aFilename,
           /* aSourceLine */ EmptyString(), aLineNumber, aColumnNumber,
           aSeverityFlag, category, aInnerWindowID));
     } else {
       MOZ_ALWAYS_SUCCEEDS(scriptError->Init(
           aMessage, aFilename,
           /* aSourceLine */ EmptyString(), aLineNumber, aColumnNumber,
           aSeverityFlag, category.get(),
-          /* IDB doesn't run on Private browsing mode */ false));
+          /* IDB doesn't run on Private browsing mode */ false,
+          /* from chrome context */ aIsChrome));
     }
 
     MOZ_ALWAYS_SUCCEEDS(consoleService->LogMessage(scriptError));
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(NS_IsMainThread());
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -438,17 +438,17 @@ ConsoleListener::Observe(nsIConsoleMessa
     return NS_OK;
   }
 
   nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage);
   if (scriptError) {
     nsAutoString msg, sourceName, sourceLine;
     nsCString category;
     uint32_t lineNum, colNum, flags;
-    bool fromPrivateWindow;
+    bool fromPrivateWindow, fromChromeContext;
 
     nsresult rv = scriptError->GetErrorMessage(msg);
     NS_ENSURE_SUCCESS(rv, rv);
     TruncateString(msg);
     rv = scriptError->GetSourceName(sourceName);
     NS_ENSURE_SUCCESS(rv, rv);
     TruncateString(sourceName);
     rv = scriptError->GetSourceLine(sourceLine);
@@ -460,16 +460,18 @@ ConsoleListener::Observe(nsIConsoleMessa
     rv = scriptError->GetLineNumber(&lineNum);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetColumnNumber(&colNum);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetIsFromPrivateWindow(&fromPrivateWindow);
     NS_ENSURE_SUCCESS(rv, rv);
+    rv = scriptError->GetIsFromChromeContext(&fromChromeContext);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     {
       AutoJSAPI jsapi;
       jsapi.Init();
       JSContext* cx = jsapi.cx();
 
       JS::RootedValue stack(cx);
       rv = scriptError->GetStack(&stack);
@@ -491,25 +493,25 @@ ConsoleListener::Observe(nsIConsoleMessa
           return err.StealNSResult();
         }
 
         ClonedMessageData cloned;
         if (!data.BuildClonedMessageDataForChild(mChild, cloned)) {
           return NS_ERROR_FAILURE;
         }
 
-        mChild->SendScriptErrorWithStack(msg, sourceName, sourceLine, lineNum,
-                                         colNum, flags, category,
-                                         fromPrivateWindow, cloned);
+        mChild->SendScriptErrorWithStack(
+            msg, sourceName, sourceLine, lineNum, colNum, flags, category,
+            fromPrivateWindow, fromChromeContext, cloned);
         return NS_OK;
       }
     }
 
     mChild->SendScriptError(msg, sourceName, sourceLine, lineNum, colNum, flags,
-                            category, fromPrivateWindow);
+                            category, fromPrivateWindow, fromChromeContext);
     return NS_OK;
   }
 
   nsString msg;
   nsresult rv = aMessage->GetMessageMoz(msg);
   NS_ENSURE_SUCCESS(rv, rv);
   mChild->SendConsoleMessage(msg);
   return NS_OK;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4095,39 +4095,40 @@ mozilla::ipc::IPCResult ContentParent::R
   consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvScriptError(
     const nsString& aMessage, const nsString& aSourceName,
     const nsString& aSourceLine, const uint32_t& aLineNumber,
     const uint32_t& aColNumber, const uint32_t& aFlags,
-    const nsCString& aCategory, const bool& aFromPrivateWindow) {
+    const nsCString& aCategory, const bool& aFromPrivateWindow,
+    const bool& aFromChromeContext) {
   return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
                                  aLineNumber, aColNumber, aFlags, aCategory,
-                                 aFromPrivateWindow);
+                                 aFromPrivateWindow, aFromChromeContext);
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
     const nsString& aMessage, const nsString& aSourceName,
     const nsString& aSourceLine, const uint32_t& aLineNumber,
     const uint32_t& aColNumber, const uint32_t& aFlags,
     const nsCString& aCategory, const bool& aFromPrivateWindow,
-    const ClonedMessageData& aFrame) {
-  return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
-                                 aLineNumber, aColNumber, aFlags, aCategory,
-                                 aFromPrivateWindow, &aFrame);
+    const bool& aFromChromeContext, const ClonedMessageData& aFrame) {
+  return RecvScriptErrorInternal(
+      aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber, aFlags,
+      aCategory, aFromPrivateWindow, aFromChromeContext, &aFrame);
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
     const nsString& aMessage, const nsString& aSourceName,
     const nsString& aSourceLine, const uint32_t& aLineNumber,
     const uint32_t& aColNumber, const uint32_t& aFlags,
     const nsCString& aCategory, const bool& aFromPrivateWindow,
-    const ClonedMessageData* aStack) {
+    const bool& aFromChromeContext, const ClonedMessageData* aStack) {
   RefPtr<nsConsoleService> consoleService = GetConsoleService();
   if (!consoleService) {
     return IPC_OK();
   }
 
   nsCOMPtr<nsIScriptError> msg;
 
   if (aStack) {
@@ -4152,19 +4153,19 @@ mozilla::ipc::IPCResult ContentParent::R
     MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj));
 
     JS::RootedObject stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
     msg = new nsScriptErrorWithStack(stackObj, stackGlobal);
   } else {
     msg = new nsScriptError();
   }
 
-  nsresult rv =
-      msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber,
-                aFlags, aCategory.get(), aFromPrivateWindow);
+  nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber,
+                          aColNumber, aFlags, aCategory.get(),
+                          aFromPrivateWindow, aFromChromeContext);
   if (NS_FAILED(rv)) return IPC_OK();
 
   consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvPrivateDocShellsExist(
     const bool& aExist) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1002,31 +1002,33 @@ class ContentParent final : public PCont
   mozilla::ipc::IPCResult RecvSetGeolocationHigherAccuracy(const bool& aEnable);
 
   mozilla::ipc::IPCResult RecvConsoleMessage(const nsString& aMessage);
 
   mozilla::ipc::IPCResult RecvScriptError(
       const nsString& aMessage, const nsString& aSourceName,
       const nsString& aSourceLine, const uint32_t& aLineNumber,
       const uint32_t& aColNumber, const uint32_t& aFlags,
-      const nsCString& aCategory, const bool& aIsFromPrivateWindow);
+      const nsCString& aCategory, const bool& aIsFromPrivateWindow,
+      const bool& aIsFromChromeContext);
 
   mozilla::ipc::IPCResult RecvScriptErrorWithStack(
       const nsString& aMessage, const nsString& aSourceName,
       const nsString& aSourceLine, const uint32_t& aLineNumber,
       const uint32_t& aColNumber, const uint32_t& aFlags,
       const nsCString& aCategory, const bool& aIsFromPrivateWindow,
-      const ClonedMessageData& aStack);
+      const bool& aIsFromChromeContext, const ClonedMessageData& aStack);
 
  private:
   mozilla::ipc::IPCResult RecvScriptErrorInternal(
       const nsString& aMessage, const nsString& aSourceName,
       const nsString& aSourceLine, const uint32_t& aLineNumber,
       const uint32_t& aColNumber, const uint32_t& aFlags,
       const nsCString& aCategory, const bool& aIsFromPrivateWindow,
+      const bool& aIsFromChromeContext,
       const ClonedMessageData* aStack = nullptr);
 
  public:
   mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist);
 
   mozilla::ipc::IPCResult RecvCommitBrowsingContextTransaction(
       BrowsingContext* aContext, BrowsingContext::Transaction&& aTransaction,
       BrowsingContext::FieldEpochs&& aEpochs);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -931,21 +931,22 @@ parent:
 
     async AddGeolocationListener(Principal principal, bool highAccuracy);
     async RemoveGeolocationListener();
     async SetGeolocationHigherAccuracy(bool enable);
 
     async ConsoleMessage(nsString message);
     async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                       uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
-                      nsCString category, bool privateWindow);
+                      nsCString category, bool privateWindow,
+                      bool fromChromeContext);
     async ScriptErrorWithStack(nsString message, nsString sourceName, nsString sourceLine,
                                uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
                                nsCString category, bool privateWindow,
-                               ClonedMessageData stack);
+                               bool fromChromeContext, ClonedMessageData stack);
 
     // Places the items within dataTransfer on the clipboard.
     async SetClipboard(IPCDataTransfer aDataTransfer,
                        bool aIsPrivateData,
                        Principal aRequestingPrincipal,
                        uint32_t aContentPolicyType,
                        int32_t aWhichClipboard);
 
--- a/dom/localstorage/ReportInternalError.cpp
+++ b/dom/localstorage/ReportInternalError.cpp
@@ -21,14 +21,14 @@ void ReportInternalError(const char* aFi
     if (*p == '/' && *(p + 1)) {
       aFile = p + 1;
     }
   }
 
   nsContentUtils::LogSimpleConsoleError(
       NS_ConvertUTF8toUTF16(
           nsPrintfCString("LocalStorage %s: %s:%" PRIu32, aStr, aFile, aLine)),
-      "localstorage", false);
+      "localstorage", false, true /* Internal errors are chrome context only*/);
 }
 
 }  // namespace localstorage
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -87,16 +87,20 @@ namespace dom {
 
 // 0 is a special value that MediaStreams use to denote they are not part of a
 // AudioContext.
 static dom::AudioContext::AudioContextId gAudioContextId = 1;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext)
+  // The destination node and AudioContext form a cycle and so the destination
+  // stream will be destroyed.  mWorklet must be shut down before the stream
+  // is destroyed.  Do this before clearing mWorklet.
+  tmp->ShutdownWorklet();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseGripArray)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingResumePromises)
   if (!tmp->mIsStarted) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes)
   }
@@ -627,16 +631,22 @@ MediaStreamGraph* AudioContext::Graph() 
 
 AudioNodeStream* AudioContext::DestinationStream() const {
   if (Destination()) {
     return Destination()->Stream();
   }
   return nullptr;
 }
 
+void AudioContext::ShutdownWorklet() {
+  if (mWorklet) {
+    mWorklet->Impl()->NotifyWorkletFinished();
+  }
+}
+
 double AudioContext::CurrentTime() {
   MediaStream* stream = Destination()->Stream();
 
   double rawTime = stream->StreamTimeToSeconds(stream->GetCurrentTime());
 
   // CurrentTime increments in intervals of 128/sampleRate. If the Timer
   // Precision Reduction is smaller than this interval, the jittered time
   // can always be reversed to the raw step of the interval. In that case
@@ -698,16 +708,22 @@ void AudioContext::Shutdown() {
     mPendingResumePromises.Clear();
   }
 
   // Release references to active nodes.
   // Active AudioNodes don't unregister in destructors, at which point the
   // Node is already unregistered.
   mActiveNodes.Clear();
 
+  // On process shutdown, the MSG thread shuts down before the destination
+  // stream is destroyed, but AudioWorklet needs to release objects on the MSG
+  // thread.  AudioContext::Shutdown() is invoked on processing the
+  // PBrowser::Destroy() message before xpcom shutdown begins.
+  ShutdownWorklet();
+
   // For offline contexts, we can destroy the MediaStreamGraph at this point.
   if (mIsOffline && mDestination) {
     mDestination->OfflineShutdown();
   }
 }
 
 StateChangeTask::StateChangeTask(AudioContext* aAudioContext, void* aPromise,
                                  AudioContextState aNewState)
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -305,16 +305,17 @@ class AudioContext final : public DOMEve
 
   void RegisterNode(AudioNode* aNode);
   void UnregisterNode(AudioNode* aNode);
 
   void OnStateChanged(void* aPromise, AudioContextState aNewState);
 
   BasicWaveFormCache* GetBasicWaveFormCache();
 
+  void ShutdownWorklet();
   // Steals from |aParamMap|
   void SetParamMapForWorkletName(const nsAString& aName,
                                  AudioParamDescriptorMap* aParamMap);
   const AudioParamDescriptorMap* GetParamMapForWorkletName(
       const nsAString& aName) {
     return mWorkletParamDescriptors.GetValue(aName);
   }
 
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -396,16 +396,18 @@ void AudioDestinationNode::DestroyAudioC
   }
 }
 
 void AudioDestinationNode::DestroyMediaStream() {
   DestroyAudioChannelAgent();
 
   if (!mStream) return;
 
+  Context()->ShutdownWorklet();
+
   mStream->RemoveMainThreadListener(this);
   MediaStreamGraph* graph = mStream->Graph();
   if (graph->IsNonRealtime()) {
     MediaStreamGraph::DestroyNonRealtimeInstance(graph);
   }
   AudioNode::DestroyMediaStream();
 }
 
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -55,16 +55,17 @@ skip-if = toolkit == 'android' # needs p
 [test_bug854082.html]
 [test_bug863792.html]
 [test_bug967694.html]
 [test_bug985859.html]
 [test_bug986930.html]
 [test_bug1092842.html]
 skip-if = (verify && (os == 'win'))
 [test_bug1165981.html]
+skip-if = (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [test_bug1245545.html]
 [test_bug1307694.html]
 skip-if = verify
 [test_cocoa_focus.html]
 skip-if = toolkit != "cocoa" || e10s # Bug 1194534
 support-files = cocoa_focus.html
 [test_cocoa_window_focus.html]
 skip-if = toolkit != "cocoa" # Bug 1194534
@@ -88,16 +89,17 @@ skip-if = (verify && !debug && (os == 'l
 skip-if = !crashreporter || e10s
 [test_hidden_plugin.html]
 [test_instance_re-parent.html]
 [test_instance_unparent1.html]
 [test_instance_unparent2.html]
 [test_instance_unparent3.html]
 [test_instantiation.html]
 [test_mixed_case_mime.html]
+skip-if = (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [test_multipleinstanceobjects.html]
 [test_newstreamondestroy.html]
 [test_npn_timers.html]
 [test_npobject_getters.html]
 [test_NPNVdocumentOrigin.html]
 [test_NPPVpluginWantsAllNetworkStreams.html]
 [test_npruntime_construct.html]
 [test_npruntime_identifiers.html]
--- a/dom/plugins/test/unit/xpcshell.ini
+++ b/dom/plugins/test/unit/xpcshell.ini
@@ -16,10 +16,10 @@ fail-if = os == "android"
 [test_nice_plugin_name.js]
 # Bug 676953: test fails consistently on Android
 fail-if = os == "android"
 [test_persist_in_prefs.js]
 skip-if = appname == "thunderbird"
 reason = plugins are disabled by default in Thunderbird
 [test_bug854467.js]
 [test_plugin_default_state.js]
-skip-if = appname == "thunderbird"
-reason = plugins are disabled by default in Thunderbird
+skip-if = appname == "thunderbird" || (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
+reason = plugins are disabled by default in Thunderbird and Windows10-aarch64
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -11,16 +11,17 @@ support-files =
 
 [test_has_permissions.html]
 [test_permissions.html]
 [test_register.html]
 skip-if = os == "win" # Bug 1373346
 [test_register_key.html]
 [test_multiple_register.html]
 [test_multiple_register_during_service_activation.html]
+skip-if = (os == "win") || (os == "linux") || (os == "mac") #Bug 1274773
 [test_unregister.html]
 [test_multiple_register_different_scope.html]
 [test_subscription_change.html]
 skip-if = os == "win" # Bug 1373346
 [test_data.html]
 skip-if = os == "win" # Bug 1373346
 [test_try_registering_offline_disabled.html]
 skip-if = os == "win" # Bug 1373346
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1363,18 +1363,18 @@ void ReportInternalError(const char* aFi
     if (*p == '/' && *(p + 1)) {
       aFile = p + 1;
     }
   }
 
   nsContentUtils::LogSimpleConsoleError(
       NS_ConvertUTF8toUTF16(
           nsPrintfCString("Quota %s: %s:%" PRIu32, aStr, aFile, aLine)),
-      "quota",
-      false /* Quota Manager is not active in private browsing mode */);
+      "quota", false /* Quota Manager is not active in private browsing mode */,
+      true /* Quota Manager runs always in a chrome context */);
 }
 
 namespace {
 
 StaticAutoPtr<nsString> gBaseDirPath;
 
 #ifdef DEBUG
 bool gQuotaManagerInitialized = false;
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -154,17 +154,18 @@ void CSP_LogMessage(const nsAString& aMe
 
   nsresult rv;
   if (aInnerWindowID > 0) {
     rv = error->InitWithWindowID(cspMsg, aSourceName, aSourceLine, aLineNumber,
                                  aColumnNumber, aFlags, category,
                                  aInnerWindowID);
   } else {
     rv = error->Init(cspMsg, aSourceName, aSourceLine, aLineNumber,
-                     aColumnNumber, aFlags, category.get(), aFromPrivateWindow);
+                     aColumnNumber, aFlags, category.get(), aFromPrivateWindow,
+                     true /* from chrome context */);
   }
   if (NS_FAILED(rv)) {
     return;
   }
   console->LogMessage(error);
 }
 
 /**
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -65,17 +65,17 @@ skip-if = toolkit == 'android' #TIMED_OU
 [test_bug393974.html]
 [test_bug394769.html]
 [test_bug396843.html]
 [test_bug400204.html]
 [test_bug404748.html]
 [test_bug414291.html]
 tags = openwindow
 [test_bug427744.html]
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' || (processor == 'aarch64' && os == 'win') # aarch64 due to 1538785
 [test_bug42976.html]
 [test_bug430276.html]
 [test_bug440572.html]
 [test_bug456151.html]
 [test_bug458091.html]
 skip-if = toolkit == 'android' && !is_fennec # Bug 1525959
 [test_bug459848.html]
 [test_bug465263.html]
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -46,18 +46,26 @@ NS_NAMED_LITERAL_STRING(
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 NS_INTERFACE_MAP_END_INHERITING(WebAuthnManagerBase)
 
 NS_IMPL_ADDREF_INHERITED(U2F, WebAuthnManagerBase)
 NS_IMPL_RELEASE_INHERITED(U2F, WebAuthnManagerBase)
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(U2F, WebAuthnManagerBase,
-                                                mTransaction)
+NS_IMPL_CYCLE_COLLECTION_CLASS(U2F)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(U2F, WebAuthnManagerBase)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->ClearTransaction();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(U2F, WebAuthnManagerBase)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(U2F)
 
 /***********************************************************************
  * Utility Functions
  **********************************************************************/
 
 static ErrorCode ConvertNSResultToErrorCode(const nsresult& aError) {
   if (aError == NS_ERROR_DOM_TIMEOUT_ERR) {
     return ErrorCode::TIMEOUT;
@@ -508,17 +516,17 @@ void U2F::FinishGetAssertion(const uint6
   nsMainThreadPtrHandle<U2FSignCallback> callback(
       mTransaction.ref().GetSignCallback());
 
   ClearTransaction();
   ExecuteCallback(response, callback);
 }
 
 void U2F::ClearTransaction() {
-  if (!NS_WARN_IF(mTransaction.isNothing())) {
+  if (!mTransaction.isNothing()) {
     StopListeningForVisibilityEvents();
   }
 
   mTransaction.reset();
 }
 
 void U2F::RejectTransaction(const nsresult& aError) {
   if (NS_WARN_IF(mTransaction.isNothing())) {
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -136,20 +136,21 @@ class U2F final : public WebAuthnManager
 
  private:
   MOZ_CAN_RUN_SCRIPT ~U2F();
 
   template <typename T, typename C>
   MOZ_CAN_RUN_SCRIPT void ExecuteCallback(T& aResp,
                                           nsMainThreadPtrHandle<C>& aCb);
 
+  // Rejects the current transaction and clears it.
+  MOZ_CAN_RUN_SCRIPT void RejectTransaction(const nsresult& aError);
+
   // Clears all information we have about the current transaction.
   void ClearTransaction();
-  // Rejects the current transaction and clears it.
-  MOZ_CAN_RUN_SCRIPT void RejectTransaction(const nsresult& aError);
 
   nsString mOrigin;
 
   // The current transaction, if any.
   Maybe<U2FTransaction> mTransaction;
 };
 
 inline void ImplCycleCollectionTraverse(
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -33,18 +33,28 @@ namespace dom {
 
 namespace {
 static mozilla::LazyLogModule gWebAuthnManagerLog("webauthnmanager");
 }
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(WebAuthnManager,
                                                WebAuthnManagerBase)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(WebAuthnManager, WebAuthnManagerBase,
-                                   mFollowingSignal, mTransaction)
+NS_IMPL_CYCLE_COLLECTION_CLASS(WebAuthnManager)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebAuthnManager,
+                                                WebAuthnManagerBase)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFollowingSignal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
+  tmp->ClearTransaction();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebAuthnManager,
+                                                  WebAuthnManagerBase)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFollowingSignal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 /***********************************************************************
  * Utility Functions
  **********************************************************************/
 
 static nsresult AssembleClientData(
     const nsAString& aOrigin, const CryptoBuffer& aChallenge,
     const nsAString& aType,
@@ -151,17 +161,17 @@ nsresult RelaxSameOrigin(nsPIDOMWindowIn
   return NS_OK;
 }
 
 /***********************************************************************
  * WebAuthnManager Implementation
  **********************************************************************/
 
 void WebAuthnManager::ClearTransaction() {
-  if (!NS_WARN_IF(mTransaction.isNothing())) {
+  if (!mTransaction.isNothing()) {
     StopListeningForVisibilityEvents();
   }
 
   mTransaction.reset();
   Unfollow();
 }
 
 void WebAuthnManager::RejectTransaction(const nsresult& aError) {
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -111,20 +111,21 @@ class WebAuthnManager final : public Web
   // parent) and rejects it by calling RejectTransaction().
   void CancelTransaction(const nsresult& aError);
   // Upon a visibility change, makes note of it in the current transaction.
   void HandleVisibilityChange() override;
 
  private:
   virtual ~WebAuthnManager();
 
+  // Rejects the current transaction and calls ClearTransaction().
+  void RejectTransaction(const nsresult& aError);
+
   // Clears all information we have about the current transaction.
   void ClearTransaction();
-  // Rejects the current transaction and calls ClearTransaction().
-  void RejectTransaction(const nsresult& aError);
 
   // The current transaction, if any.
   Maybe<WebAuthnTransaction> mTransaction;
 };
 
 inline void ImplCycleCollectionTraverse(
     nsCycleCollectionTraversalCallback& aCallback,
     WebAuthnTransaction& aTransaction, const char* aName, uint32_t aFlags = 0) {
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -102,22 +102,24 @@ dictionary ConsoleEvent {
   // consumers expect to see own properties on it, which would mean making the
   // props unforgeable, which means lots of JSFunction allocations.  Maybe we
   // should fix those consumers, of course....
   // sequence<ConsoleStackEntry> stacktrace;
   DOMString groupName = "";
   any timer = null;
   any counter = null;
   DOMString prefix = "";
+  boolean chromeContext = false;
 };
 
 // Event for profile operations
 dictionary ConsoleProfileEvent {
   DOMString action = "";
   sequence<any> arguments;
+  boolean chromeContext = false;
 };
 
 // This dictionary is used to manage stack trace data.
 dictionary ConsoleStackEntry {
   DOMString filename = "";
   // Unique identifier within the process for the script source this entry is
   // associated with, or zero.
   unsigned long sourceId = 0;
--- a/dom/websocket/WebSocket.cpp
+++ b/dom/websocket/WebSocket.cpp
@@ -97,16 +97,17 @@ class WebSocketImpl final : public nsIIn
         mDisconnectingOrDisconnected(false),
         mCloseEventWasClean(false),
         mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
         mPort(0),
         mScriptLine(0),
         mScriptColumn(0),
         mInnerWindowID(0),
         mPrivateBrowsing(false),
+        mIsChromeContext(false),
         mIsMainThread(true),
         mMutex("WebSocketImpl::mMutex"),
         mWorkerShuttingDown(false) {
     if (!NS_IsMainThread()) {
       mIsMainThread = false;
     }
   }
 
@@ -196,16 +197,17 @@ class WebSocketImpl final : public nsIIn
   // - the ID of the inner window where the script lives. Note that this may not
   //   be the same as the Web Socket owner window.
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   uint32_t mScriptLine;
   uint32_t mScriptColumn;
   uint64_t mInnerWindowID;
   bool mPrivateBrowsing;
+  bool mIsChromeContext;
 
   RefPtr<ThreadSafeWorkerRef> mWorkerRef;
 
   nsWeakPtr mWeakLoadGroup;
 
   bool mIsMainThread;
 
   // This mutex protects mWorkerShuttingDown.
@@ -339,17 +341,17 @@ void WebSocketImpl::PrintErrorOnConsole(
   if (mInnerWindowID) {
     rv = errorObject->InitWithWindowID(
         message, NS_ConvertUTF8toUTF16(mScriptFile), EmptyString(), mScriptLine,
         mScriptColumn, nsIScriptError::errorFlag, "Web Socket", mInnerWindowID);
   } else {
     rv = errorObject->Init(message, NS_ConvertUTF8toUTF16(mScriptFile),
                            EmptyString(), mScriptLine, mScriptColumn,
                            nsIScriptError::errorFlag, "Web Socket",
-                           mPrivateBrowsing);
+                           mPrivateBrowsing, mIsChromeContext);
   }
 
   NS_ENSURE_SUCCESS_VOID(rv);
 
   // print the error message directly to the JS console
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS_VOID(rv);
 }
@@ -1500,16 +1502,17 @@ nsresult WebSocketImpl::Init(JSContext* 
   // If we don't have aCx, we are window-less, so we don't have a
   // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
   // DedicateWorkers created by JSM.
   if (aCx) {
     mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
   }
 
   mPrivateBrowsing = !!aPrincipal->OriginAttributesRef().mPrivateBrowsingId;
+  mIsChromeContext = nsContentUtils::IsSystemPrincipal(aPrincipal);
 
   // parses the url
   rv = ParseURL(aURL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<Document> originDoc = mWebSocket->GetDocumentIfCurrent();
   if (!originDoc) {
     rv = mWebSocket->CheckCurrentGlobalCorrectness();
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -321,34 +321,31 @@ class WorkletFetchHandler final : public
 };
 
 NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
 
 NS_IMETHODIMP
 ExecutionRunnable::Run() {
   // WorkletThread::IsOnWorkletThread() cannot be used here because it depends
   // on a WorkletJSContext having been created for this thread.  That does not
-  // happen until the first time RunOnWorkletThread() is called.
+  // happen until the global scope is created the first time
+  // RunOnWorkletThread() is called.
   if (!NS_IsMainThread()) {
     RunOnWorkletThread();
     return NS_DispatchToMainThread(this);
   }
 
   RunOnMainThread();
   return NS_OK;
 }
 
 void ExecutionRunnable::RunOnWorkletThread() {
   WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime);
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-
-  RefPtr<WorkletGlobalScope> globalScope =
-      mWorkletImpl->CreateGlobalScope(jsapi.cx());
+  WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope();
   MOZ_ASSERT(globalScope);
 
   AutoEntryScript aes(globalScope, "Worklet");
   JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
 
   NS_ConvertUTF16toUTF8 url(mHandler->URL());
--- a/dom/worklet/Worklet.h
+++ b/dom/worklet/Worklet.h
@@ -41,16 +41,18 @@ class Worklet final : public nsISupports
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise> AddModule(const nsAString& aModuleURL,
                                       const WorkletOptions& aOptions,
                                       CallerType aCallerType, ErrorResult& aRv);
 
+  WorkletImpl* Impl() const { return mImpl; }
+
  private:
   ~Worklet();
 
   WorkletFetchHandler* GetImportFetchHandler(const nsACString& aURI);
 
   void AddImportFetchHandler(const nsACString& aURI,
                              WorkletFetchHandler* aHandler);
 
--- a/dom/worklet/WorkletImpl.cpp
+++ b/dom/worklet/WorkletImpl.cpp
@@ -6,16 +6,17 @@
 
 #include "WorkletImpl.h"
 
 #include "Worklet.h"
 #include "WorkletThread.h"
 
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/RegisterWorkletBindings.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/WorkletBinding.h"
 
 namespace mozilla {
 
 // ---------------------------------------------------------------------------
 // WorkletLoadInfo
 
 WorkletLoadInfo::WorkletLoadInfo(nsPIDOMWindowInner* aWindow,
@@ -37,48 +38,64 @@ WorkletLoadInfo::~WorkletLoadInfo() {
 }
 
 // ---------------------------------------------------------------------------
 // WorkletImpl
 
 WorkletImpl::WorkletImpl(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
     : mWorkletLoadInfo(aWindow, aPrincipal), mTerminated(false) {}
 
-WorkletImpl::~WorkletImpl() = default;
+WorkletImpl::~WorkletImpl() { MOZ_ASSERT(!mGlobalScope); }
 
 JSObject* WorkletImpl::WrapWorklet(JSContext* aCx, dom::Worklet* aWorklet,
                                    JS::Handle<JSObject*> aGivenProto) {
   MOZ_ASSERT(NS_IsMainThread());
   return dom::Worklet_Binding::Wrap(aCx, aWorklet, aGivenProto);
 }
 
-already_AddRefed<dom::WorkletGlobalScope> WorkletImpl::CreateGlobalScope(
-    JSContext* aCx) {
+dom::WorkletGlobalScope* WorkletImpl::GetGlobalScope() {
   dom::WorkletThread::AssertIsOnWorkletThread();
 
-  RefPtr<dom::WorkletGlobalScope> scope = ConstructGlobalScope();
+  if (mGlobalScope) {
+    return mGlobalScope;
+  }
 
-  JS::Rooted<JSObject*> global(aCx);
-  NS_ENSURE_TRUE(scope->WrapGlobalObject(aCx, &global), nullptr);
+  dom::AutoJSAPI jsapi;
+  jsapi.Init();
+  JSContext* cx = jsapi.cx();
 
-  JSAutoRealm ar(aCx, global);
+  mGlobalScope = ConstructGlobalScope();
+
+  JS::Rooted<JSObject*> global(cx);
+  NS_ENSURE_TRUE(mGlobalScope->WrapGlobalObject(cx, &global), nullptr);
+
+  JSAutoRealm ar(cx, global);
 
   // Init Web IDL bindings
-  if (!dom::RegisterWorkletBindings(aCx, global)) {
+  if (!dom::RegisterWorkletBindings(cx, global)) {
     return nullptr;
   }
 
-  JS_FireOnNewGlobalObject(aCx, global);
+  JS_FireOnNewGlobalObject(cx, global);
 
-  return scope.forget();
+  return mGlobalScope;
 }
 
 void WorkletImpl::NotifyWorkletFinished() {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (mTerminated) {
+    return;
+  }
+
+  // Release global scope on its thread.
+  SendControlMessage(NS_NewRunnableFunction(
+      "WorkletImpl::NotifyWorkletFinished",
+      [self = RefPtr<WorkletImpl>(this)]() { self->mGlobalScope = nullptr; }));
+
   mTerminated = true;
   if (mWorkletThread) {
     mWorkletThread->Terminate();
     mWorkletThread = nullptr;
   }
   mWorkletLoadInfo.mPrincipal = nullptr;
 }
 
@@ -88,17 +105,17 @@ nsresult WorkletImpl::SendControlMessage
 
   // TODO: bug 1492011 re ConsoleWorkletRunnable.
   if (mTerminated) {
     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   }
 
   if (!mWorkletThread) {
     // Thread creation. FIXME: this will change.
-    mWorkletThread = dom::WorkletThread::Create();
+    mWorkletThread = dom::WorkletThread::Create(this);
     if (!mWorkletThread) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   return mWorkletThread->DispatchRunnable(std::move(aRunnable));
 }
 
--- a/dom/worklet/WorkletImpl.h
+++ b/dom/worklet/WorkletImpl.h
@@ -69,17 +69,17 @@ class WorkletImpl {
   virtual JSObject* WrapWorklet(JSContext* aCx, dom::Worklet* aWorklet,
                                 JS::Handle<JSObject*> aGivenProto);
 
   virtual nsresult SendControlMessage(already_AddRefed<nsIRunnable> aRunnable);
 
   void NotifyWorkletFinished();
 
   // Execution thread only.
-  already_AddRefed<dom::WorkletGlobalScope> CreateGlobalScope(JSContext* aCx);
+  dom::WorkletGlobalScope* GetGlobalScope();
 
   // Any thread.
 
   const WorkletLoadInfo& LoadInfo() const { return mWorkletLoadInfo; }
 
  protected:
   WorkletImpl(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
   virtual ~WorkletImpl();
@@ -88,13 +88,16 @@ class WorkletImpl {
 
   // The only WorkletLoadInfo member modified is mPrincipal which is accessed
   // on only the parent thread.
   WorkletLoadInfo mWorkletLoadInfo;
 
   // Parent thread only.
   RefPtr<dom::WorkletThread> mWorkletThread;
   bool mTerminated;
+
+  // Execution thread only.
+  RefPtr<dom::WorkletGlobalScope> mGlobalScope;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_dom_worklet_WorkletImpl_h
--- a/dom/worklet/WorkletThread.cpp
+++ b/dom/worklet/WorkletThread.cpp
@@ -205,34 +205,36 @@ class WorkletThread::TerminateRunnable f
     mWorkletThread->TerminateInternal();
     return NS_OK;
   }
 
  private:
   RefPtr<WorkletThread> mWorkletThread;
 };
 
-WorkletThread::WorkletThread()
+WorkletThread::WorkletThread(WorkletImpl* aWorkletImpl)
     : nsThread(MakeNotNull<ThreadEventQueue<mozilla::EventQueue>*>(
                    MakeUnique<mozilla::EventQueue>()),
                nsThread::NOT_MAIN_THREAD, kWorkletStackSize),
+      mWorkletImpl(aWorkletImpl),
       mExitLoop(false),
       mIsTerminating(false) {
   MOZ_ASSERT(NS_IsMainThread());
   nsContentUtils::RegisterShutdownObserver(this);
 }
 
 WorkletThread::~WorkletThread() {
   // This should be set during the termination step.
   MOZ_ASSERT(mExitLoop);
 }
 
 // static
-already_AddRefed<WorkletThread> WorkletThread::Create() {
-  RefPtr<WorkletThread> thread = new WorkletThread();
+already_AddRefed<WorkletThread> WorkletThread::Create(
+    WorkletImpl* aWorkletImpl) {
+  RefPtr<WorkletThread> thread = new WorkletThread(aWorkletImpl);
   if (NS_WARN_IF(NS_FAILED(thread->Init()))) {
     return nullptr;
   }
 
   RefPtr<PrimaryRunnable> runnable = new PrimaryRunnable(thread);
   if (NS_WARN_IF(NS_FAILED(thread->DispatchRunnable(runnable.forget())))) {
     return nullptr;
   }
@@ -363,16 +365,18 @@ void WorkletThread::AssertIsOnWorkletThr
 }
 
 // nsIObserver
 NS_IMETHODIMP
 WorkletThread::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t*) {
   MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
 
-  Terminate();
+  // The WorkletImpl will terminate the worklet thread after sending a message
+  // to release worklet thread objects.
+  mWorkletImpl->NotifyWorkletFinished();
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(WorkletThread, nsThread, nsIObserver)
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/worklet/WorkletThread.h
+++ b/dom/worklet/WorkletThread.h
@@ -6,46 +6,47 @@
 
 #ifndef mozilla_dom_worklet_WorkletThread_h
 #define mozilla_dom_worklet_WorkletThread_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/dom/WorkletImpl.h"
 #include "nsThread.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
 class WorkletThread final : public nsThread, public nsIObserver {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIOBSERVER
 
-  static already_AddRefed<WorkletThread> Create();
+  static already_AddRefed<WorkletThread> Create(WorkletImpl* aWorkletImpl);
 
   // Threads that call EnsureCycleCollectedJSContext must call
   // DeleteCycleCollectedJSContext::Get() before terminating.  Clients of
   // Create() do not need to do this as Terminate() will ensure this happens.
   static void EnsureCycleCollectedJSContext(JSRuntime* aParentRuntime);
   static void DeleteCycleCollectedJSContext();
 
   static bool IsOnWorkletThread();
 
   static void AssertIsOnWorkletThread();
 
   nsresult DispatchRunnable(already_AddRefed<nsIRunnable> aRunnable);
 
   void Terminate();
 
  private:
-  WorkletThread();
+  explicit WorkletThread(WorkletImpl* aWorkletImpl);
   ~WorkletThread();
 
   void RunEventLoop();
   class PrimaryRunnable;
 
   void TerminateInternal();
   class TerminateRunnable;
 
@@ -55,16 +56,18 @@ class WorkletThread final : public nsThr
   Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override;
 
   NS_IMETHOD
   DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
 
   NS_IMETHOD
   DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
 
+  const RefPtr<WorkletImpl> mWorkletImpl;
+
   bool mExitLoop;  // worklet execution thread
 
   bool mIsTerminating;  // main thread
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -83,19 +83,17 @@ UniquePtr<SurfaceFactory> GLScreenBuffer
     factory = SurfaceFactory_IOSurface::Create(gl, caps, ipcChannel, flags);
 #elif defined(MOZ_X11)
     if (sGLXLibrary.UseTextureFromPixmap())
       factory = SurfaceFactory_GLXDrawable::Create(gl, caps, ipcChannel, flags);
 #elif defined(MOZ_WIDGET_UIKIT)
     factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, ipcChannel,
                                                    mFlags);
 #elif defined(MOZ_WIDGET_ANDROID)
-    // XXX WebRender does not support SurfaceFactory_EGLImage usage.
-    if (XRE_IsParentProcess() && !gfxPrefs::WebGLSurfaceTextureEnabled() &&
-        backend != layers::LayersBackend::LAYERS_WR) {
+    if (XRE_IsParentProcess() && !gfxPrefs::WebGLSurfaceTextureEnabled()) {
       factory = SurfaceFactory_EGLImage::Create(gl, caps, ipcChannel, flags);
     } else {
       factory =
           SurfaceFactory_SurfaceTexture::Create(gl, caps, ipcChannel, flags);
     }
 #else
     if (gl->GetContextType() == GLContextType::EGL) {
       if (XRE_IsParentProcess()) {
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -11,16 +11,17 @@
 #include "GLLibraryEGL.h"  // for GLLibraryEGL
 #include "GLUploadHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "gfx2DGlue.h"             // for ContentForFormat, etc
 #include "mozilla/gfx/2D.h"        // for DataSourceSurface
 #include "mozilla/gfx/BaseSize.h"  // for BaseSize
 #include "mozilla/gfx/Logging.h"   // for gfxCriticalError
 #include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/webrender/RenderEGLImageTextureHost.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "nsRegion.h"  // for nsIntRegion
 #include "AndroidSurfaceTexture.h"
 #include "GfxTexturesReporter.h"  // for GfxTexturesReporter
 #include "GLBlitTextureImageHelper.h"
 #include "GeckoProfiler.h"
 
 #ifdef XP_MACOSX
@@ -812,16 +813,54 @@ void EGLImageTextureHost::SetTextureSour
 }
 
 gfx::SurfaceFormat EGLImageTextureHost::GetFormat() const {
   MOZ_ASSERT(mTextureSource);
   return mTextureSource ? mTextureSource->GetFormat()
                         : gfx::SurfaceFormat::UNKNOWN;
 }
 
+void EGLImageTextureHost::CreateRenderTexture(
+    const wr::ExternalImageId& aExternalImageId) {
+  RefPtr<wr::RenderTextureHost> texture =
+      new wr::RenderEGLImageTextureHost(mImage, mSync, mSize);
+  wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId),
+                                                 texture.forget());
+}
+
+void EGLImageTextureHost::PushResourceUpdates(
+    wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+    const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+  auto method = aOp == TextureHost::ADD_IMAGE
+                    ? &wr::TransactionBuilder::AddExternalImage
+                    : &wr::TransactionBuilder::UpdateExternalImage;
+  auto bufferType = wr::WrExternalImageBufferType::TextureExternalHandle;
+
+  gfx::SurfaceFormat format =
+      mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8;
+
+  MOZ_ASSERT(aImageKeys.length() == 1);
+  // XXX Add RGBA handling. Temporary hack to avoid crash
+  // With BGRA format setting, rendering works without problem.
+  auto formatTmp = format == gfx::SurfaceFormat::R8G8B8A8
+                       ? gfx::SurfaceFormat::B8G8R8A8
+                       : gfx::SurfaceFormat::B8G8R8X8;
+  wr::ImageDescriptor descriptor(GetSize(), formatTmp);
+  (aResources.*method)(aImageKeys[0], descriptor, aExtID, bufferType, 0);
+}
+
+void EGLImageTextureHost::PushDisplayItems(
+    wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
+    const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+    const Range<wr::ImageKey>& aImageKeys) {
+  MOZ_ASSERT(aImageKeys.length() == 1);
+  aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0],
+                     !(mFlags & TextureFlags::NON_PREMULTIPLIED));
+}
+
 //
 
 GLTextureHost::GLTextureHost(TextureFlags aFlags, GLuint aTextureHandle,
                              GLenum aTarget, GLsync aSync, gfx::IntSize aSize,
                              bool aHasAlpha)
     : TextureHost(aFlags),
       mTexture(aTextureHandle),
       mTarget(aTarget),
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -540,16 +540,30 @@ class EGLImageTextureHost final : public
   }
 
   gl::GLContext* gl() const;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual const char* Name() override { return "EGLImageTextureHost"; }
 
+  virtual void CreateRenderTexture(
+      const wr::ExternalImageId& aExternalImageId) override;
+
+  virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
+                                   ResourceUpdateOp aOp,
+                                   const Range<wr::ImageKey>& aImageKeys,
+                                   const wr::ExternalImageId& aExtID) override;
+
+  virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+                                const wr::LayoutRect& aBounds,
+                                const wr::LayoutRect& aClip,
+                                wr::ImageRendering aFilter,
+                                const Range<wr::ImageKey>& aImageKeys) override;
+
  protected:
   const EGLImage mImage;
   const EGLSync mSync;
   const gfx::IntSize mSize;
   const bool mHasAlpha;
   RefPtr<EGLImageTextureSource> mTextureSource;
 };
 
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/gfx/2D.h"
 
 using namespace mozilla;
 
 nsFont::nsFont(const FontFamilyList& aFontlist, nscoord aSize)
     : fontlist(aFontlist), size(aSize) {}
 
-nsFont::nsFont(FontFamilyType aGenericType, nscoord aSize)
+nsFont::nsFont(StyleGenericFontFamily aGenericType, nscoord aSize)
     : fontlist(aGenericType), size(aSize) {}
 
 nsFont::nsFont(const nsFont& aOther) = default;
 
 nsFont::nsFont() {}
 
 nsFont::~nsFont() {}
 
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -16,32 +16,16 @@
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/RefPtr.h"  // for RefPtr
 #include "nsColor.h"         // for nsColor and NS_RGBA
 #include "nsCoord.h"         // for nscoord
 #include "nsTArray.h"        // for nsTArray
 
 struct gfxFontStyle;
 
-// IDs for generic fonts
-// NOTE: 0, 1 are reserved for the special IDs of the default variable
-// and fixed fonts in the presentation context, see nsPresContext.h
-const uint8_t kGenericFont_NONE = 0x00;
-// Special
-const uint8_t kGenericFont_moz_variable =
-    0x00;  // for the default variable width font
-const uint8_t kGenericFont_moz_fixed =
-    0x01;  // our special "use the user's fixed font"
-// CSS
-const uint8_t kGenericFont_serif = 0x02;
-const uint8_t kGenericFont_sans_serif = 0x04;
-const uint8_t kGenericFont_monospace = 0x08;
-const uint8_t kGenericFont_cursive = 0x10;
-const uint8_t kGenericFont_fantasy = 0x20;
-
 // Font structure.
 struct nsFont {
   typedef mozilla::FontStretch FontStretch;
   typedef mozilla::FontSlantStyle FontSlantStyle;
   typedef mozilla::FontWeight FontWeight;
 
   // List of font families, either named or generic.
   // This contains a RefPtr and a uint32_t field.
@@ -112,17 +96,17 @@ struct nsFont {
   // Force this font to not be considered a 'generic' font, even if
   // the name is the same as a CSS generic font family.
   bool systemFont = false;
 
   // initialize the font with a fontlist
   nsFont(const mozilla::FontFamilyList& aFontlist, nscoord aSize);
 
   // initialize the font with a single generic
-  nsFont(mozilla::FontFamilyType aGenericType, nscoord aSize);
+  nsFont(mozilla::StyleGenericFontFamily, nscoord aSize);
 
   // Make a copy of the given font
   nsFont(const nsFont& aFont);
 
   // leave members uninitialized
   nsFont();
 
   ~nsFont();
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -1967,25 +1967,20 @@ bool gfxFcPlatformFontList::GetStandardF
     }
   }
 
   // didn't find localized name, leave family name blank
   return true;
 }
 
 void gfxFcPlatformFontList::AddGenericFonts(
-    mozilla::FontFamilyType aGenericType, nsAtom* aLanguage,
+    mozilla::StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
     nsTArray<FamilyAndGeneric>& aFamilyList) {
   bool usePrefFontList = false;
 
-  // treat -moz-fixed as monospace
-  if (aGenericType == eFamily_moz_fixed) {
-    aGenericType = eFamily_monospace;
-  }
-
   const char* generic = GetGenericName(aGenericType);
   NS_ASSERTION(generic, "weird generic font type");
   if (!generic) {
     return;
   }
 
   // By default, most font prefs on Linux map to "use fontconfig"
   // keywords. So only need to explicitly lookup font pref if
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -296,17 +296,17 @@ class gfxFcPlatformFontList : public gfx
                           gfxFloat aDevToCssSize = 1.0) override;
 
   bool GetStandardFamilyName(const nsCString& aFontName,
                              nsACString& aFamilyName) override;
 
   FcConfig* GetLastConfig() const { return mLastConfig; }
 
   // override to use fontconfig lookup for generics
-  void AddGenericFonts(mozilla::FontFamilyType aGenericType, nsAtom* aLanguage,
+  void AddGenericFonts(mozilla::StyleGenericFontFamily, nsAtom* aLanguage,
                        nsTArray<FamilyAndGeneric>& aFamilyList) override;
 
   void ClearLangGroupPrefFonts() override;
 
   // clear out cached generic-lang ==> family-list mappings
   void ClearGenericMappings() { mGenericMappings.Clear(); }
 
   // map lang group ==> lang string
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxFont.h"
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/IntegerRange.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/SVGContextPaint.h"
 
 #include "mozilla/Logging.h"
 
 #include "nsITimer.h"
 
@@ -3995,28 +3996,29 @@ bool gfxFont::TryGetMathTable() {
 
   return !!mMathTable;
 }
 
 /* static */
 void SharedFontList::Initialize() {
   sEmpty = new SharedFontList();
 
-  for (uint8_t i = 0; i < uint8_t(eFamily_generic_count_including_special);
-       ++i) {
-    auto type = static_cast<FontFamilyType>(i + uint8_t(eFamily_generic_first));
-    sSingleGenerics[i] = new SharedFontList(type);
+  for (auto i : IntegerRange(ArrayLength(sSingleGenerics))) {
+    auto type = static_cast<StyleGenericFontFamily>(i);
+    if (type != StyleGenericFontFamily::None) {
+      sSingleGenerics[i] = new SharedFontList(type);
+    }
   }
 }
 
 /* static */
 void SharedFontList::Shutdown() {
   sEmpty = nullptr;
 
   for (auto& sharedFontList : sSingleGenerics) {
     sharedFontList = nullptr;
   }
 }
 
 StaticRefPtr<SharedFontList> SharedFontList::sEmpty;
 
 StaticRefPtr<SharedFontList>
-    SharedFontList::sSingleGenerics[eFamily_generic_count_including_special];
+    SharedFontList::sSingleGenerics[size_t(StyleGenericFontFamily::MozEmoji)];
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -932,33 +932,33 @@ struct FontFamily {
   bool mIsShared;
 };
 
 // Struct used in the gfxFontGroup font list to keep track of a font family
 // together with the CSS generic (if any) that was mapped to it in this
 // particular case (so it can be reported to the DevTools font inspector).
 struct FamilyAndGeneric final {
   FamilyAndGeneric()
-      : mFamily(), mGeneric(mozilla::FontFamilyType::eFamily_none) {}
+      : mFamily(), mGeneric(mozilla::StyleGenericFontFamily::None) {}
   FamilyAndGeneric(const FamilyAndGeneric& aOther)
       : mFamily(aOther.mFamily), mGeneric(aOther.mGeneric) {}
-  explicit FamilyAndGeneric(
-      gfxFontFamily* aFamily,
-      mozilla::FontFamilyType aGeneric = mozilla::FontFamilyType::eFamily_none)
+  explicit FamilyAndGeneric(gfxFontFamily* aFamily,
+                            mozilla::StyleGenericFontFamily aGeneric =
+                                mozilla::StyleGenericFontFamily::None)
       : mFamily(aFamily), mGeneric(aGeneric) {}
-  explicit FamilyAndGeneric(
-      mozilla::fontlist::Family* aFamily,
-      mozilla::FontFamilyType aGeneric = mozilla::FontFamilyType::eFamily_none)
+  explicit FamilyAndGeneric(mozilla::fontlist::Family* aFamily,
+                            mozilla::StyleGenericFontFamily aGeneric =
+                                mozilla::StyleGenericFontFamily::None)
       : mFamily(aFamily), mGeneric(aGeneric) {}
-  explicit FamilyAndGeneric(
-      const FontFamily& aFamily,
-      mozilla::FontFamilyType aGeneric = mozilla::FontFamilyType::eFamily_none)
+  explicit FamilyAndGeneric(const FontFamily& aFamily,
+                            mozilla::StyleGenericFontFamily aGeneric =
+                                mozilla::StyleGenericFontFamily::None)
       : mFamily(aFamily), mGeneric(aGeneric) {}
 
   bool operator==(const FamilyAndGeneric& aOther) const {
-    return (mFamily == aOther.mFamily && mGeneric == aOther.mGeneric);
+    return mFamily == aOther.mFamily && mGeneric == aOther.mGeneric;
   }
 
   FontFamily mFamily;
-  mozilla::FontFamilyType mGeneric;
+  mozilla::StyleGenericFontFamily mGeneric;
 };
 
 #endif
--- a/gfx/thebes/gfxFontFamilyList.h
+++ b/gfx/thebes/gfxFontFamilyList.h
@@ -5,213 +5,158 @@
 
 #ifndef GFX_FONT_FAMILY_LIST_H
 #define GFX_FONT_FAMILY_LIST_H
 
 #include "nsAtom.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
+#include "nsStyleConsts.h"
 #include "nsUnicharUtils.h"
 #include "nsTArray.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 
 namespace mozilla {
 
 /**
- * type of font family name, either a name (e.g. Helvetica) or a
- * generic (e.g. serif, sans-serif), with the ability to distinguish
- * between unquoted and quoted names for serializaiton
- */
-
-enum FontFamilyType : uint8_t {
-  eFamily_none = 0,  // used when finding generics
-
-  // explicitly named font family (e.g. Helvetica)
-  eFamily_named,
-  eFamily_named_quoted,
-
-  // generics
-  eFamily_serif,  // pref font code relies on this ordering!!!
-  eFamily_sans_serif,
-  eFamily_monospace,
-  eFamily_cursive,
-  eFamily_fantasy,
-
-  // special
-  eFamily_moz_variable,
-  eFamily_moz_fixed,
-  eFamily_moz_emoji,
-
-  eFamily_generic_first = eFamily_serif,
-  eFamily_generic_last = eFamily_fantasy,
-  eFamily_generic_last_including_special = eFamily_moz_emoji,
-
-  eFamily_generic_count = eFamily_generic_last - eFamily_generic_first + 1,
-  eFamily_generic_count_including_special =
-      eFamily_generic_last_including_special - eFamily_generic_first + 1,
-};
-
-enum QuotedName { eQuotedName, eUnquotedName };
-
-/**
  * font family name, an Atom for the name if not a generic and
  * a font type indicated named family or which generic family
  */
 
 struct FontFamilyName final {
-  FontFamilyName() : mType(eFamily_none) {}
+  using Syntax = StyleFontFamilyNameSyntax;
+
+  FontFamilyName() = delete;
 
   // named font family - e.g. Helvetica
-  explicit FontFamilyName(nsAtom* aFamilyName,
-                          QuotedName aQuoted = eUnquotedName) {
-    mType = (aQuoted == eQuotedName) ? eFamily_named_quoted : eFamily_named;
-    mName = aFamilyName;
-  }
+  explicit FontFamilyName(nsAtom* aFamilyName, Syntax aSyntax)
+      : mName(aFamilyName), mSyntax(aSyntax) {}
 
-  explicit FontFamilyName(const nsACString& aFamilyName,
-                          QuotedName aQuoted = eUnquotedName) {
-    mType = (aQuoted == eQuotedName) ? eFamily_named_quoted : eFamily_named;
-    mName = NS_Atomize(aFamilyName);
-  }
+  explicit FontFamilyName(const nsACString& aFamilyName, Syntax aSyntax)
+      : mName(NS_Atomize(aFamilyName)), mSyntax(aSyntax) {}
 
   // generic font family - e.g. sans-serif
-  explicit FontFamilyName(FontFamilyType aType) {
-    NS_ASSERTION(aType != eFamily_named && aType != eFamily_named_quoted &&
-                     aType != eFamily_none,
-                 "expected a generic font type");
-    mName = nullptr;
-    mType = aType;
+  explicit FontFamilyName(StyleGenericFontFamily aGeneric)
+      : mGeneric(aGeneric) {
+    MOZ_ASSERT(mGeneric != StyleGenericFontFamily::None);
   }
 
-  FontFamilyName(const FontFamilyName& aCopy) {
-    mType = aCopy.mType;
-    mName = aCopy.mName;
-  }
+  FontFamilyName(const FontFamilyName&) = default;
 
-  bool IsNamed() const {
-    return mType == eFamily_named || mType == eFamily_named_quoted;
-  }
+  bool IsNamed() const { return !!mName; }
 
   bool IsGeneric() const { return !IsNamed(); }
 
   void AppendToString(nsACString& aFamilyList, bool aQuotes = true) const {
-    switch (mType) {
-      case eFamily_named:
-        aFamilyList.Append(nsAtomCString(mName));
-        break;
-      case eFamily_named_quoted:
-        if (aQuotes) {
-          aFamilyList.Append('"');
-        }
-        aFamilyList.Append(nsAtomCString(mName));
-        if (aQuotes) {
-          aFamilyList.Append('"');
-        }
-        break;
-      case eFamily_serif:
+    if (IsNamed()) {
+      if (mSyntax == Syntax::Identifiers) {
+        return aFamilyList.Append(nsAtomCString(mName));
+      }
+      if (aQuotes) {
+        aFamilyList.Append('"');
+      }
+      aFamilyList.Append(nsAtomCString(mName));
+      if (aQuotes) {
+        aFamilyList.Append('"');
+      }
+      return;
+    }
+    switch (mGeneric) {
+      case StyleGenericFontFamily::None:
+      case StyleGenericFontFamily::MozEmoji:
+        MOZ_FALLTHROUGH_ASSERT("Should never appear in a font-family name!");
+      case StyleGenericFontFamily::Serif:
         aFamilyList.AppendLiteral("serif");
         break;
-      case eFamily_sans_serif:
+      case StyleGenericFontFamily::SansSerif:
         aFamilyList.AppendLiteral("sans-serif");
         break;
-      case eFamily_monospace:
+      case StyleGenericFontFamily::Monospace:
         aFamilyList.AppendLiteral("monospace");
         break;
-      case eFamily_cursive:
+      case StyleGenericFontFamily::Cursive:
         aFamilyList.AppendLiteral("cursive");
         break;
-      case eFamily_fantasy:
+      case StyleGenericFontFamily::Fantasy:
         aFamilyList.AppendLiteral("fantasy");
         break;
-      case eFamily_moz_fixed:
-        aFamilyList.AppendLiteral("-moz-fixed");
-        break;
       default:
+        MOZ_ASSERT_UNREACHABLE("Unknown generic font-family!");
         break;
     }
   }
 
   // helper method that converts generic names to the right enum value
   static FontFamilyName Convert(const nsACString& aFamilyOrGenericName) {
     // should only be passed a single font - not entirely correct, a family
     // *could* have a comma in it but in practice never does so
     // for debug purposes this is fine
     NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1,
                  "Convert method should only be passed a single family name");
 
-    FontFamilyType genericType = eFamily_none;
+    auto genericType = StyleGenericFontFamily::None;
     if (aFamilyOrGenericName.LowerCaseEqualsLiteral("serif")) {
-      genericType = eFamily_serif;
+      genericType = StyleGenericFontFamily::Serif;
     } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("sans-serif")) {
-      genericType = eFamily_sans_serif;
-    } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("monospace")) {
-      genericType = eFamily_monospace;
+      genericType = StyleGenericFontFamily::SansSerif;
+    } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("monospace") ||
+               aFamilyOrGenericName.LowerCaseEqualsLiteral("-moz-fixed")) {
+      genericType = StyleGenericFontFamily::Monospace;
     } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("cursive")) {
-      genericType = eFamily_cursive;
+      genericType = StyleGenericFontFamily::Cursive;
     } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("fantasy")) {
-      genericType = eFamily_fantasy;
-    } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("-moz-fixed")) {
-      genericType = eFamily_moz_fixed;
+      genericType = StyleGenericFontFamily::Fantasy;
     } else {
-      return FontFamilyName(aFamilyOrGenericName, eUnquotedName);
+      return FontFamilyName(aFamilyOrGenericName, Syntax::Identifiers);
     }
 
     return FontFamilyName(genericType);
   }
 
-  FontFamilyType mType;
-  RefPtr<nsAtom> mName;  // null if mType != eFamily_named
+  RefPtr<nsAtom> mName;  // null if mGeneric != Default
+  StyleFontFamilyNameSyntax mSyntax = StyleFontFamilyNameSyntax::Quoted;
+  StyleGenericFontFamily mGeneric = StyleGenericFontFamily::None;
 };
 
 inline bool operator==(const FontFamilyName& a, const FontFamilyName& b) {
-  return a.mType == b.mType && a.mName == b.mName;
+  return a.mName == b.mName && a.mSyntax == b.mSyntax &&
+         a.mGeneric == b.mGeneric;
 }
 
 /**
  * A refcounted array of FontFamilyNames.  We use this to store the specified
  * and computed value of the font-family property.
  *
  * TODO(heycam): It might better to define this type (and FontFamilyList and
  * FontFamilyName) in Rust.
  */
 class SharedFontList {
+  using Syntax = StyleFontFamilyNameSyntax;
+
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedFontList);
 
-  SharedFontList() {}
+  SharedFontList() = default;
 
-  explicit SharedFontList(FontFamilyType aGenericType)
+  explicit SharedFontList(StyleGenericFontFamily aGenericType)
       : mNames{FontFamilyName(aGenericType)} {}
 
-  SharedFontList(nsAtom* aFamilyName, QuotedName aQuoted)
-      : mNames{FontFamilyName(aFamilyName, aQuoted)} {}
+  SharedFontList(nsAtom* aFamilyName, Syntax aSyntax)
+      : mNames{FontFamilyName(aFamilyName, aSyntax)} {}
 
-  SharedFontList(const nsACString& aFamilyName, QuotedName aQuoted)
-      : mNames{FontFamilyName(aFamilyName, aQuoted)} {}
-
-  explicit SharedFontList(const FontFamilyName& aName) : mNames{aName} {}
+  SharedFontList(const nsACString& aFamilyName, Syntax aSyntax)
+      : mNames{FontFamilyName(aFamilyName, aSyntax)} {}
 
   explicit SharedFontList(nsTArray<FontFamilyName>&& aNames)
       : mNames(std::move(aNames)) {}
 
-  FontFamilyType FirstGeneric() const {
-    for (const FontFamilyName& name : mNames) {
-      if (name.IsGeneric()) {
-        return name.mType;
-      }
-    }
-    return eFamily_none;
-  }
-
-  bool HasGeneric() const { return FirstGeneric() != eFamily_none; }
-
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     size_t n = 0;
     n += aMallocSizeOf(this);
     n += mNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
     return n;
   }
 
   size_t SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const {
@@ -223,59 +168,51 @@ class SharedFontList {
   }
 
   const nsTArray<FontFamilyName> mNames;
 
   static void Initialize();
   static void Shutdown();
   static StaticRefPtr<SharedFontList> sEmpty;
   static StaticRefPtr<SharedFontList>
-      sSingleGenerics[eFamily_generic_count_including_special];
+      sSingleGenerics[size_t(StyleGenericFontFamily::MozEmoji)];
 
  private:
   ~SharedFontList() = default;
 };
 
 /**
  * font family list, array of font families and a default font type.
  * font family names are either named strings or generics. the default
  * font type is used to preserve the variable font fallback behavior
  */
 class FontFamilyList {
+  using Syntax = StyleFontFamilyNameSyntax;
+
  public:
-  FontFamilyList()
-      : mFontlist(WrapNotNull(SharedFontList::sEmpty.get())),
-        mDefaultFontType(eFamily_none) {}
-
-  explicit FontFamilyList(FontFamilyType aGenericType)
-      : mFontlist(MakeNotNull<SharedFontList*>(aGenericType)),
-        mDefaultFontType(eFamily_none) {}
+  FontFamilyList() : mFontlist(WrapNotNull(SharedFontList::sEmpty.get())) {}
 
-  FontFamilyList(nsAtom* aFamilyName, QuotedName aQuoted)
-      : mFontlist(MakeNotNull<SharedFontList*>(aFamilyName, aQuoted)),
-        mDefaultFontType(eFamily_none) {}
+  explicit FontFamilyList(StyleGenericFontFamily aGenericType)
+      : mFontlist(MakeNotNull<SharedFontList*>(aGenericType)) {}
 
-  FontFamilyList(const nsACString& aFamilyName, QuotedName aQuoted)
-      : mFontlist(MakeNotNull<SharedFontList*>(aFamilyName, aQuoted)),
-        mDefaultFontType(eFamily_none) {}
+  FontFamilyList(nsAtom* aFamilyName, Syntax aSyntax)
+      : mFontlist(MakeNotNull<SharedFontList*>(aFamilyName, aSyntax)) {}
 
-  explicit FontFamilyList(const FontFamilyName& aName)
-      : mFontlist(MakeNotNull<SharedFontList*>(aName)),
-        mDefaultFontType(eFamily_none) {}
+  FontFamilyList(const nsACString& aFamilyName, Syntax aSyntax)
+      : mFontlist(MakeNotNull<SharedFontList*>(aFamilyName, aSyntax)) {}
 
   explicit FontFamilyList(nsTArray<FontFamilyName>&& aNames)
-      : mFontlist(MakeNotNull<SharedFontList*>(std::move(aNames))),
-        mDefaultFontType(eFamily_none) {}
+      : mFontlist(MakeNotNull<SharedFontList*>(std::move(aNames))) {}
 
   FontFamilyList(const FontFamilyList& aOther)
       : mFontlist(aOther.mFontlist),
         mDefaultFontType(aOther.mDefaultFontType) {}
 
   explicit FontFamilyList(NotNull<SharedFontList*> aFontList)
-      : mFontlist(aFontList), mDefaultFontType(eFamily_none) {}
+      : mFontlist(aFontList) {}
 
   void SetFontlist(nsTArray<FontFamilyName>&& aNames) {
     mFontlist = MakeNotNull<SharedFontList*>(std::move(aNames));
   }
 
   void SetFontlist(NotNull<SharedFontList*> aFontlist) {
     mFontlist = aFontlist;
   }
@@ -287,77 +224,77 @@ class FontFamilyList {
   NotNull<SharedFontList*> GetFontlist() const { return mFontlist; }
 
   bool Equals(const FontFamilyList& aFontlist) const {
     return (mFontlist == aFontlist.mFontlist ||
             mFontlist->mNames == aFontlist.mFontlist->mNames) &&
            mDefaultFontType == aFontlist.mDefaultFontType;
   }
 
-  FontFamilyType FirstGeneric() const { return mFontlist->FirstGeneric(); }
-
-  bool HasGeneric() const { return mFontlist->HasGeneric(); }
-
   bool HasDefaultGeneric() const {
+    if (mDefaultFontType == StyleGenericFontFamily::None) {
+      return false;
+    }
     for (const FontFamilyName& name : mFontlist->mNames) {
-      if (name.mType == mDefaultFontType) {
+      if (name.mGeneric == mDefaultFontType) {
         return true;
       }
     }
     return false;
   }
 
   // Find the first generic (but ignoring cursive and fantasy, as they are
   // rarely configured in any useful way) in the list.
   // If found, move it to the start and return true; else return false.
   bool PrioritizeFirstGeneric() {
     uint32_t len = mFontlist->mNames.Length();
     for (uint32_t i = 0; i < len; i++) {
       const FontFamilyName name = mFontlist->mNames[i];
       if (name.IsGeneric()) {
-        if (name.mType == eFamily_cursive || name.mType == eFamily_fantasy) {
+        if (name.mGeneric == StyleGenericFontFamily::Cursive ||
+            name.mGeneric == StyleGenericFontFamily::Fantasy) {
           continue;
         }
         if (i > 0) {
           nsTArray<FontFamilyName> names;
           names.AppendElements(mFontlist->mNames);
           names.RemoveElementAt(i);
           names.InsertElementAt(0, name);
           SetFontlist(std::move(names));
         }
         return true;
       }
     }
     return false;
   }
 
-  void PrependGeneric(FontFamilyType aType) {
+  void PrependGeneric(StyleGenericFontFamily aGeneric) {
     nsTArray<FontFamilyName> names;
     names.AppendElements(mFontlist->mNames);
-    names.InsertElementAt(0, FontFamilyName(aType));
+    names.InsertElementAt(0, FontFamilyName(aGeneric));
     SetFontlist(std::move(names));
   }
 
   void ToString(nsACString& aFamilyList, bool aQuotes = true,
                 bool aIncludeDefault = false) const {
     const nsTArray<FontFamilyName>& names = mFontlist->mNames;
     aFamilyList.Truncate();
     uint32_t len = names.Length();
     for (uint32_t i = 0; i < len; i++) {
       if (i != 0) {
         aFamilyList.Append(',');
       }
       const FontFamilyName& name = names[i];
       name.AppendToString(aFamilyList, aQuotes);
     }
-    if (aIncludeDefault && mDefaultFontType != eFamily_none) {
+    if (aIncludeDefault && mDefaultFontType != StyleGenericFontFamily::None) {
       if (!aFamilyList.IsEmpty()) {
         aFamilyList.Append(',');
       }
-      if (mDefaultFontType == eFamily_serif) {
+      if (mDefaultFontType == StyleGenericFontFamily::Serif) {
         aFamilyList.AppendLiteral("serif");
       } else {
         aFamilyList.AppendLiteral("sans-serif");
       }
     }
   }
 
   // searches for a specific non-generic name, case-insensitive comparison
@@ -370,20 +307,21 @@ class FontFamilyList {
       nsDependentAtomString listname(name.mName);
       if (listname.Equals(fam, nsCaseInsensitiveStringComparator())) {
         return true;
       }
     }
     return false;
   }
 
-  FontFamilyType GetDefaultFontType() const { return mDefaultFontType; }
-  void SetDefaultFontType(FontFamilyType aType) {
-    NS_ASSERTION(aType == eFamily_none || aType == eFamily_serif ||
-                     aType == eFamily_sans_serif,
+  StyleGenericFontFamily GetDefaultFontType() const { return mDefaultFontType; }
+  void SetDefaultFontType(StyleGenericFontFamily aType) {
+    NS_ASSERTION(aType == StyleGenericFontFamily::None ||
+                     aType == StyleGenericFontFamily::Serif ||
+                     aType == StyleGenericFontFamily::SansSerif,
                  "default font type must be either serif or sans-serif");
     mDefaultFontType = aType;
   }
 
   // memory reporting
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
     size_t n = 0;
     n += mFontlist->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
@@ -391,17 +329,18 @@ class FontFamilyList {
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
  protected:
   NotNull<RefPtr<SharedFontList>> mFontlist;
-  FontFamilyType mDefaultFontType;  // none, serif or sans-serif
+  StyleGenericFontFamily mDefaultFontType =
+      StyleGenericFontFamily::None;  // or serif, or sans-serif
 };
 
 inline bool operator==(const FontFamilyList& a, const FontFamilyList& b) {
   return a.Equals(b);
 }
 
 inline bool operator!=(const FontFamilyList& a, const FontFamilyList& b) {
   return !a.Equals(b);
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -886,17 +886,17 @@ void gfxPlatformFontList::RemoveCmap(con
   CharMapHashKey* found =
       mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
   if (found && found->GetKey() == aCharMap) {
     mSharedCmaps.RemoveEntry(found);
   }
 }
 
 void gfxPlatformFontList::ResolveGenericFontNames(
-    FontFamilyType aGenericType, eFontPrefLang aPrefLang,
+    StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang,
     PrefFontList* aGenericFamilies) {
   const char* langGroupStr = GetPrefLangName(aPrefLang);
   const char* generic = GetGenericName(aGenericType);
 
   if (!generic) {
     return;
   }
 
@@ -952,44 +952,40 @@ void gfxPlatformFontList::GetFontFamilie
       if (!aGenericFamilies->Contains(f.mFamily)) {
         aGenericFamilies->AppendElement(f.mFamily);
       }
     }
   }
 }
 
 gfxPlatformFontList::PrefFontList* gfxPlatformFontList::GetPrefFontsLangGroup(
-    mozilla::FontFamilyType aGenericType, eFontPrefLang aPrefLang) {
-  // treat -moz-fixed as monospace
-  if (aGenericType == eFamily_moz_fixed) {
-    aGenericType = eFamily_monospace;
-  }
-
-  if (aGenericType == eFamily_moz_emoji) {
+    StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang) {
+  if (aGenericType == StyleGenericFontFamily::MozEmoji) {
     // Emoji font has no lang
     PrefFontList* prefFonts = mEmojiPrefFont.get();
     if (MOZ_UNLIKELY(!prefFonts)) {
       prefFonts = new PrefFontList;
       ResolveEmojiFontNames(prefFonts);
       mEmojiPrefFont.reset(prefFonts);
     }
     return prefFonts;
   }
 
-  PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][aGenericType].get();
+  auto index = static_cast<size_t>(aGenericType);
+  PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][index].get();
   if (MOZ_UNLIKELY(!prefFonts)) {
     prefFonts = new PrefFontList;
     ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
-    mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
+    mLangGroupPrefFonts[aPrefLang][index].reset(prefFonts);
   }
   return prefFonts;
 }
 
 void gfxPlatformFontList::AddGenericFonts(
-    mozilla::FontFamilyType aGenericType, nsAtom* aLanguage,
+    mozilla::StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
     nsTArray<FamilyAndGeneric>& aFamilyList) {
   // map lang ==> langGroup
   nsAtom* langGroup = GetLangGroup(aLanguage);
 
   // langGroup ==> prefLang
   eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
 
   // lookup pref fonts
@@ -1277,42 +1273,42 @@ void gfxPlatformFontList::AppendPrefLang
   }
 
   if (i == aLen) {
     aPrefLangs[aLen] = aAddLang;
     aLen++;
   }
 }
 
-mozilla::FontFamilyType gfxPlatformFontList::GetDefaultGeneric(
+mozilla::StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
     eFontPrefLang aLang) {
   if (aLang == eFontPrefLang_Emoji) {
-    return eFamily_moz_emoji;
+    return StyleGenericFontFamily::MozEmoji;
   }
 
   // initialize lang group pref font defaults (i.e. serif/sans-serif)
   if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
     mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
     for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
       nsAutoCString prefDefaultFontType("font.default.");
       prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
       nsAutoCString serifOrSans;
       Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
       if (serifOrSans.EqualsLiteral("sans-serif")) {
-        mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
+        mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
       } else {
-        mDefaultGenericsLangGroup[i] = eFamily_serif;
+        mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
       }
     }
   }
 
   if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
     return mDefaultGenericsLangGroup[uint32_t(aLang)];
   }
-  return eFamily_serif;
+  return StyleGenericFontFamily::Serif;
 }
 
 FontFamily gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle) {
   FontFamily family = GetDefaultFontForPlatform(aStyle);
   if (!family.mIsShared && family.mUnshared) {
     return family;
   }
   // Something has gone wrong and we were unable to retrieve a default font
@@ -1338,50 +1334,40 @@ nsAtom* gfxPlatformFontList::GetLangGrou
   }
   if (!langGroup) {
     langGroup = nsGkAtoms::Unicode;
   }
   return langGroup;
 }
 
 /* static */ const char* gfxPlatformFontList::GetGenericName(
-    FontFamilyType aGenericType) {
+    StyleGenericFontFamily aGenericType) {
   static const char kGeneric_serif[] = "serif";
   static const char kGeneric_sans_serif[] = "sans-serif";
   static const char kGeneric_monospace[] = "monospace";
   static const char kGeneric_cursive[] = "cursive";
   static const char kGeneric_fantasy[] = "fantasy";
 
   // type should be standard generic type at this point
-  NS_ASSERTION(aGenericType >= eFamily_serif && aGenericType <= eFamily_fantasy,
-               "standard generic font family type required");
-
   // map generic type to string
-  const char* generic = nullptr;
   switch (aGenericType) {
-    case eFamily_serif:
-      generic = kGeneric_serif;
-      break;
-    case eFamily_sans_serif:
-      generic = kGeneric_sans_serif;
-      break;
-    case eFamily_monospace:
-      generic = kGeneric_monospace;
-      break;
-    case eFamily_cursive:
-      generic = kGeneric_cursive;
-      break;
-    case eFamily_fantasy:
-      generic = kGeneric_fantasy;
-      break;
+    case StyleGenericFontFamily::Serif:
+      return kGeneric_serif;
+    case StyleGenericFontFamily::SansSerif:
+      return kGeneric_sans_serif;
+    case StyleGenericFontFamily::Monospace:
+      return kGeneric_monospace;
+    case StyleGenericFontFamily::Cursive:
+      return kGeneric_cursive;
+    case StyleGenericFontFamily::Fantasy:
+      return kGeneric_fantasy;
     default:
-      break;
+      MOZ_ASSERT_UNREACHABLE("Unknown generic");
+      return nullptr;
   }
-
-  return generic;
 }
 
 void gfxPlatformFontList::InitLoader() {
   GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
   mStartIndex = 0;
   mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
   memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
 }
@@ -1499,19 +1485,18 @@ void gfxPlatformFontList::RebuildLocalFo
     it.Get()->GetKey()->RebuildLocalRules();
   }
 }
 
 void gfxPlatformFontList::ClearLangGroupPrefFonts() {
   for (uint32_t i = eFontPrefLang_First;
        i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
     auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
-    for (uint32_t j = eFamily_generic_first;
-         j < eFamily_generic_first + eFamily_generic_count; j++) {
-      prefFontsLangGroup[j] = nullptr;
+    for (auto& pref : prefFontsLangGroup) {
+      pref = nullptr;
     }
   }
   mCJKPrefLangs.Clear();
   mEmojiPrefFont = nullptr;
 }
 
 // Support for memory reporting
 
@@ -1559,19 +1544,17 @@ void gfxPlatformFontList::AddSizeOfExclu
         mExtraNames->mFullnames, aMallocSizeOf);
     aSizes->mFontListSize += SizeOfFontEntryTableExcludingThis(
         mExtraNames->mPostscriptNames, aMallocSizeOf);
   }
 
   for (uint32_t i = eFontPrefLang_First;
        i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
     auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
-    for (uint32_t j = eFamily_generic_first;
-         j < eFamily_generic_first + eFamily_generic_count; j++) {
-      PrefFontList* pf = prefFontsLangGroup[j].get();
+    for (const UniquePtr<PrefFontList>& pf : prefFontsLangGroup) {
       if (pf) {
         aSizes->mFontListSize += pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
       }
     }
   }
 
   aSizes->mFontListSize +=
       mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -253,22 +253,22 @@ class gfxPlatformFontList : public gfxFo
 
   static const gfxFontEntry::ScriptRange sComplexScriptRanges[];
 
   void GetFontlistInitInfo(uint32_t& aNumInits, uint32_t& aLoaderState) {
     aNumInits = mFontlistInitCount;
     aLoaderState = (uint32_t)mState;
   }
 
-  virtual void AddGenericFonts(mozilla::FontFamilyType aGenericType,
+  virtual void AddGenericFonts(mozilla::StyleGenericFontFamily aGenericType,
                                nsAtom* aLanguage,
                                nsTArray<FamilyAndGeneric>& aFamilyList);
 
-  PrefFontList* GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
-                                      eFontPrefLang aPrefLang);
+  PrefFontList* GetPrefFontsLangGroup(
+      mozilla::StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang);
 
   // in some situations, need to make decisions about ambiguous characters, may
   // need to look at multiple pref langs
   void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t& aLen,
                     eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
   // convert a lang group to enum constant (i.e. "zh-TW" ==>
   // eFontPrefLang_ChineseTW)
@@ -290,27 +290,28 @@ class gfxPlatformFontList : public gfxFo
   // returns true if a pref lang is CJK
   static bool IsLangCJK(eFontPrefLang aLang);
 
   // helper method to add a pref lang to an array, if not already in array
   static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen,
                              eFontPrefLang aAddLang);
 
   // default serif/sans-serif choice based on font.default.xxx prefs
-  mozilla::FontFamilyType GetDefaultGeneric(eFontPrefLang aLang);
+  mozilla::StyleGenericFontFamily GetDefaultGeneric(eFontPrefLang aLang);
 
   // Returns true if the font family whitelist is not empty.
   bool IsFontFamilyWhitelistActive();
 
   static void FontWhitelistPrefChanged(const char* aPref, void* aClosure);
 
   bool AddWithLegacyFamilyName(const nsACString& aLegacyName,
                                gfxFontEntry* aFontEntry);
 
-  static const char* GetGenericName(mozilla::FontFamilyType aGenericType);
+  static const char* GetGenericName(
+      mozilla::StyleGenericFontFamily aGenericType);
 
  protected:
   class InitOtherFamilyNamesRunnable : public mozilla::CancelableRunnable {
    public:
     InitOtherFamilyNamesRunnable()
         : CancelableRunnable(
               "gfxPlatformFontList::InitOtherFamilyNamesRunnable"),
           mIsCanceled(false) {}
@@ -481,17 +482,17 @@ class gfxPlatformFontList : public gfxFo
   // read the loader initialization prefs, and start it
   void GetPrefsAndStartLoader();
 
   // for font list changes that affect all documents
   void ForceGlobalReflow() { gfxPlatform::ForceGlobalReflow(); }
 
   void RebuildLocalFonts();
 
-  void ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
+  void ResolveGenericFontNames(mozilla::StyleGenericFontFamily aGenericType,
                                eFontPrefLang aPrefLang,
                                PrefFontList* aGenericFamilies);
 
   void ResolveEmojiFontNames(PrefFontList* aGenericFamilies);
 
   void GetFontFamiliesFromGenericFamilies(nsTArray<nsCString>& aGenericFamilies,
                                           nsAtom* aLangGroup,
                                           PrefFontList* aFontFamilies);
@@ -547,18 +548,19 @@ class gfxPlatformFontList : public gfxFo
 
   // face names missed when face name loading takes a long time
   mozilla::UniquePtr<nsTHashtable<nsCStringHashKey>> mFaceNamesMissed;
 
   // localized family names missed when face name loading takes a long time
   mozilla::UniquePtr<nsTHashtable<nsCStringHashKey>> mOtherNamesMissed;
 
   typedef mozilla::RangedArray<mozilla::UniquePtr<PrefFontList>,
-                               mozilla::eFamily_generic_first,
-                               mozilla::eFamily_generic_count>
+                               size_t(mozilla::StyleGenericFontFamily::None),
+                               size_t(
+                                   mozilla::StyleGenericFontFamily::MozEmoji)>
       PrefFontsForLangGroup;
   mozilla::RangedArray<PrefFontsForLangGroup, eFontPrefLang_First,
                        eFontPrefLang_Count>
       mLangGroupPrefFonts;
   mozilla::UniquePtr<PrefFontList> mEmojiPrefFont;
 
   // when system-wide font lookup fails for a character, cache it to skip future
   // searches
@@ -584,16 +586,16 @@ class gfxPlatformFontList : public gfxFo
   // see bugs 636957, 1070983, 1189129
   uint32_t mFontlistInitCount;  // num times InitFontList called
 
   nsTHashtable<nsPtrHashKey<gfxUserFontSet>> mUserFontSetList;
 
   nsLanguageAtomService* mLangService;
 
   nsTArray<uint32_t> mCJKPrefLangs;
-  nsTArray<mozilla::FontFamilyType> mDefaultGenericsLangGroup;
+  nsTArray<mozilla::StyleGenericFontFamily> mDefaultGenericsLangGroup;
 
   bool mFontFamilyWhitelistActive;
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxPlatformFontList::FindFamiliesFlags)
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1530,17 +1530,17 @@ void gfxTextRun::SetSpaceGlyph(gfxFont* 
   bool vertical =
       !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
   gfxFontShaper::RoundingFlags roundingFlags =
       aFont->GetRoundOffsetsToPixels(aDrawTarget);
   gfxShapedWord* sw = aFont->GetShapedWord(
       aDrawTarget, &space, 1, gfxShapedWord::HashMix(0, ' '), Script::LATIN,
       vertical, mAppUnitsPerDevUnit, flags, roundingFlags, nullptr);
   if (sw) {
-    AddGlyphRun(aFont, FontMatchType::kFontGroup, aCharIndex, false,
+    AddGlyphRun(aFont, FontMatchType::Kind::kFontGroup, aCharIndex, false,
                 aOrientation);
     CopyGlyphDataFrom(sw, aCharIndex);
     GetCharacterGlyphs()[aCharIndex].SetIsSpace();
   }
 }
 
 bool gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
                                        char16_t aSpaceChar,
@@ -1555,17 +1555,17 @@ bool gfxTextRun::SetSpaceGlyphIfSimple(g
           ? nsFontMetrics::eVertical
           : nsFontMetrics::eHorizontal;
   uint32_t spaceWidthAppUnits = NS_lroundf(
       aFont->GetMetrics(fontOrientation).spaceWidth * mAppUnitsPerDevUnit);
   if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
     return false;
   }
 
-  AddGlyphRun(aFont, FontMatchType::kFontGroup, aCharIndex, false,
+  AddGlyphRun(aFont, FontMatchType::Kind::kFontGroup, aCharIndex, false,
               aOrientation);
   CompressedGlyph g =
       CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
   if (aSpaceChar == ' ') {
     g.SetIsSpace();
   }
   GetCharacterGlyphs()[aCharIndex] = g;
   return true;
@@ -1710,25 +1710,25 @@ void gfxFontGroup::BuildFontList() {
   for (const FontFamilyName& name : mFamilyList.GetFontlist()->mNames) {
     if (name.IsNamed()) {
       if (name.mName) {
         AddPlatformFont(nsAtomCString(name.mName), fonts);
       } else {
         MOZ_ASSERT_UNREACHABLE("broken FontFamilyName, no atom!");
       }
     } else {
-      pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
+      pfl->AddGenericFonts(name.mGeneric, mStyle.language, fonts);
       if (mTextPerf) {
         mTextPerf->current.genericLookups++;
       }
     }
   }
 
   // if necessary, append default generic onto the end
-  if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+  if (mFamilyList.GetDefaultFontType() != StyleGenericFontFamily::None &&
       !mFamilyList.HasDefaultGeneric()) {
     pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(), mStyle.language,
                          fonts);
     if (mTextPerf) {
       mTextPerf->current.genericLookups++;
     }
   }
 
@@ -1756,17 +1756,17 @@ void gfxFontGroup::AddPlatformFont(const
 
   // Not known in the user font set ==> check system fonts
   gfxPlatformFontList::PlatformFontList()->FindAndAddFamilies(
       aName, &aFamilyList, gfxPlatformFontList::FindFamiliesFlags(0), &mStyle,
       mDevToCssSize);
 }
 
 void gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily,
-                                       FontFamilyType aGeneric) {
+                                       StyleGenericFontFamily aGeneric) {
   if (!aFamily) {
     MOZ_ASSERT_UNREACHABLE("don't try to add a null font family!");
     return;
   }
   AutoTArray<gfxFontEntry*, 4> fontEntryList;
   aFamily->FindAllFontsForStyle(mStyle, fontEntryList);
   // add these to the fontlist
   for (gfxFontEntry* fe : fontEntryList) {
@@ -1958,17 +1958,17 @@ gfxFont* gfxFontGroup::GetDefaultFont() 
                    familiesString.get());
     MOZ_CRASH_UNSAFE(msg);
   }
 
   return mDefaultFont.get();
 }
 
 gfxFont* gfxFontGroup::GetFirstValidFont(uint32_t aCh,
-                                         FontFamilyType* aGeneric) {
+                                         StyleGenericFontFamily* aGeneric) {
   uint32_t count = mFonts.Length();
   for (uint32_t i = 0; i < count; ++i) {
     FamilyFace& ff = mFonts[i];
     if (ff.IsInvalid()) {
       continue;
     }
 
     // already have a font?
@@ -2001,17 +2001,17 @@ gfxFont* gfxFontGroup::GetFirstValidFont
     if (font) {
       if (aGeneric) {
         *aGeneric = mFonts[i].Generic();
       }
       return font;
     }
   }
   if (aGeneric) {
-    *aGeneric = eFamily_none;
+    *aGeneric = StyleGenericFontFamily::None;
   }
   return GetDefaultFont();
 }
 
 gfxFont* gfxFontGroup::GetFirstMathFont() {
   uint32_t count = mFonts.Length();
   for (uint32_t i = 0; i < count; ++i) {
     gfxFont* font = GetFontAt(i);
@@ -2073,17 +2073,17 @@ already_AddRefed<gfxTextRun> gfxFontGrou
   }
 
   gfxFont* font = GetFirstValidFont();
   if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
       MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
     // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
     // them, and always create at least size 1 fonts, i.e. they still
     // render something for size 0 fonts.
-    textRun->AddGlyphRun(font, FontMatchType::kFontGroup, 0, false,
+    textRun->AddGlyphRun(font, FontMatchType::Kind::kFontGroup, 0, false,
                          orientation);
   } else {
     if (font->GetSpaceGlyph()) {
       // Normally, the font has a cached space glyph, so we can avoid
       // the cost of calling FindFontForChar.
       textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
     } else {
       // In case the primary font doesn't have <space> (bug 970891),
@@ -2111,18 +2111,18 @@ already_AddRefed<gfxTextRun> gfxFontGrou
   if (!textRun) {
     return nullptr;
   }
 
   gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
   if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
     orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
   }
-  textRun->AddGlyphRun(GetFirstValidFont(), FontMatchType::kFontGroup, 0, false,
-                       orientation);
+  textRun->AddGlyphRun(GetFirstValidFont(), FontMatchType::Kind::kFontGroup, 0,
+                       false, orientation);
   return textRun.forget();
 }
 
 already_AddRefed<gfxTextRun> gfxFontGroup::MakeHyphenTextRun(
     DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit) {
   // only use U+2010 if it is supported by the first font in the group;
   // it's better to use ASCII '-' from the primary font than to fall back to
   // U+2010 from some other, possibly poorly-matching face
@@ -2269,19 +2269,20 @@ void gfxFontGroup::InitTextRun(DrawTarge
         nsAutoString styleString;
         nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
         MOZ_LOG(
             log, LogLevel::Warning,
             ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
              "len %d weight: %g stretch: %g%% style: %s size: %6.2f %zu-byte "
              "TEXTRUN [%s] ENDTEXTRUN\n",
              (mStyle.systemFont ? "textrunui" : "textrun"), families.get(),
-             (mFamilyList.GetDefaultFontType() == eFamily_serif
+             (mFamilyList.GetDefaultFontType() == StyleGenericFontFamily::Serif
                   ? "serif"
-                  : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
+                  : (mFamilyList.GetDefaultFontType() ==
+                             StyleGenericFontFamily::SansSerif
                          ? "sans-serif"
                          : "none")),
              lang.get(), static_cast<int>(Script::LATIN), aLength,
              mStyle.weight.ToFloat(), mStyle.stretch.Percentage(),
              NS_ConvertUTF16toUTF8(styleString).get(), mStyle.size, sizeof(T),
              str.get()));
       }
 
@@ -2315,19 +2316,21 @@ void gfxFontGroup::InitTextRun(DrawTarge
           nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
           uint32_t runLen = runLimit - runStart;
           MOZ_LOG(
               log, LogLevel::Warning,
               ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
                "len %d weight: %g stretch: %g%% style: %s size: %6.2f "
                "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
                (mStyle.systemFont ? "textrunui" : "textrun"), families.get(),
-               (mFamilyList.GetDefaultFontType() == eFamily_serif
+               (mFamilyList.GetDefaultFontType() ==
+                        StyleGenericFontFamily::Serif
                     ? "serif"
-                    : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
+                    : (mFamilyList.GetDefaultFontType() ==
+                               StyleGenericFontFamily::SansSerif
                            ? "sans-serif"
                            : "none")),
                lang.get(), static_cast<int>(runScript), runLen,
                mStyle.weight.ToFloat(), mStyle.stretch.Percentage(),
                NS_ConvertUTF16toUTF8(styleString).get(), mStyle.size, sizeof(T),
                NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
         }
 
@@ -2495,17 +2498,17 @@ void gfxFontGroup::InitScriptRun(DrawTar
         if (!matchedFont->SplitAndInitTextRun(
                 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
                 matchedLength, aRunScript, range.orientation)) {
           // glyph layout failed! treat as missing glyphs
           matchedFont = nullptr;
         }
       }
     } else {
-      aTextRun->AddGlyphRun(mainFont, FontMatchType::kFontGroup,
+      aTextRun->AddGlyphRun(mainFont, FontMatchType::Kind::kFontGroup,
                             aOffset + runStart, (matchedLength > 0),
                             range.orientation);
     }
 
     if (!matchedFont) {
       // We need to set cluster boundaries (and mark spaces) so that
       // surrogate pairs, combining characters, etc behave properly,
       // even if we don't have glyphs for them
@@ -2702,34 +2705,32 @@ gfxFont* gfxFontGroup::FindFontForChar(u
   bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
   bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
   bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
 
   if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
     gfxFont* firstFont = GetFontAt(0, aCh);
     if (firstFont) {
       if (firstFont->HasCharacter(aCh)) {
-        *aMatchType =
-            FontMatchType::kFontGroup | FontMatchType(mFonts[0].Generic());
+        *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
         return firstFont;
       }
 
       gfxFont* font = nullptr;
       if (mFonts[0].CheckForFallbackFaces()) {
         font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
       } else if (!firstFont->GetFontEntry()->IsUserFont()) {
         // For platform fonts (but not userfonts), we may need to do
         // fallback within the family to handle cases where some faces
         // such as Italic or Black have reduced character sets compared
         // to the family's Regular face.
         font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
       }
       if (font) {
-        *aMatchType =
-            FontMatchType::kFontGroup | FontMatchType(mFonts[0].Generic());
+        *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
         return font;
       }
     }
 
     // we don't need to check the first font again below
     ++nextIndex;
   }
 
@@ -2769,17 +2770,17 @@ gfxFont* gfxFontGroup::FindFontForChar(u
     if (ff.IsInvalid() || ff.IsLoading()) {
       continue;
     }
 
     // if available, use already made gfxFont and check for character
     gfxFont* font = ff.Font();
     if (font) {
       if (font->HasCharacter(aCh)) {
-        *aMatchType = FontMatchType::kFontGroup | FontMatchType(ff.Generic());
+        *aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
         return font;
       }
       continue;
     }
 
     // don't have a gfxFont yet, test before building
     gfxFontEntry* fe = ff.FontEntry();
     if (fe->mIsUserFontContainer) {
@@ -2798,96 +2799,94 @@ gfxFont* gfxFontGroup::FindFontForChar(u
           !mSkipDrawing && !FontLoadingForFamily(ff.Family(), aCh)) {
         ufe->Load();
         ff.CheckState(mSkipDrawing);
       }
       gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
       if (pfe && pfe->HasCharacter(aCh)) {
         font = GetFontAt(i, aCh);
         if (font) {
-          *aMatchType =
-              FontMatchType::kFontGroup | FontMatchType(mFonts[i].Generic());
+          *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[i].Generic()};
           return font;
         }
       }
     } else if (fe->HasCharacter(aCh)) {
       // for normal platform fonts, after checking the cmap
       // build the font via GetFontAt
       font = GetFontAt(i, aCh);
       if (font) {
-        *aMatchType =
-            FontMatchType::kFontGroup | FontMatchType(mFonts[i].Generic());
+        *aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[i].Generic()};
         return font;
       }
     }
 
     // check other family faces if needed
     if (ff.CheckForFallbackFaces()) {
       NS_ASSERTION(i == 0 ? true
                           : !mFonts[i - 1].CheckForFallbackFaces() ||
                                 !mFonts[i - 1].Family()->Name().Equals(
                                     ff.Family()->Name()),
                    "should only do fallback once per font family");
       font = FindFallbackFaceForChar(ff.Family(), aCh);
       if (font) {
-        *aMatchType = FontMatchType::kFontGroup | FontMatchType(ff.Generic());
+        *aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
         return font;
       }
     } else {
       // For platform fonts, but not user fonts, consider intra-family
       // fallback to handle styles with reduced character sets (see
       // also above).
       fe = ff.FontEntry();
       if (!fe->mIsUserFontContainer && !fe->IsUserFont()) {
         font = FindFallbackFaceForChar(ff.Family(), aCh);
         if (font) {
-          *aMatchType = FontMatchType::kFontGroup | FontMatchType(ff.Generic());
+          *aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
           return font;
         }
       }
     }
   }
 
   if (fontListLength == 0) {
     gfxFont* defaultFont = GetDefaultFont();
     if (defaultFont->HasCharacter(aCh)) {
-      *aMatchType = FontMatchType::kFontGroup;
+      *aMatchType = FontMatchType::Kind::kFontGroup;
       return defaultFont;
     }
   }
 
   // if character is in Private Use Area, don't do matching against pref or
   // system fonts
   if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
     return nullptr;
 
   // 2. search pref fonts
   gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
   if (font) {
-    *aMatchType = FontMatchType::kPrefsFallback;
+    *aMatchType = FontMatchType::Kind::kPrefsFallback;
     return font;
   }
 
   // 3. use fallback fonts
   // -- before searching for something else check the font used for the
   //    previous character
   if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
-    *aMatchType = FontMatchType::kSystemFallback;
+    *aMatchType = FontMatchType::Kind::kSystemFallback;
     return aPrevMatchedFont;
   }
 
   // for known "space" characters, don't do a full system-fallback search;
   // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
   if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
       GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0) {
     return nullptr;
   }
 
   // -- otherwise look for other stuff
-  *aMatchType = FontMatchType::kSystemFallback;
+  *aMatchType = FontMatchType::Kind::kSystemFallback;
   return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
 }
 
 template <typename T>
 void gfxFontGroup::ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString,
                                  uint32_t aLength, Script aRunScript,
                                  gfx::ShapedTextFlags aOrientation) {
   NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
@@ -2901,22 +2900,22 @@ void gfxFontGroup::ComputeRanges(nsTArra
       nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
     }
   }
   int32_t lastRangeIndex = -1;
 
   // initialize prevFont to the group's primary font, so that this will be
   // used for string-initial control chars, etc rather than risk hitting font
   // fallback for these (bug 716229)
-  FontFamilyType generic = eFamily_none;
+  StyleGenericFontFamily generic = StyleGenericFontFamily::None;
   gfxFont* prevFont = GetFirstValidFont(' ', &generic);
 
   // if we use the initial value of prevFont, we treat this as a match from
   // the font group; fixes bug 978313
-  FontMatchType matchType = FontMatchType::kFontGroup | FontMatchType(generic);
+  FontMatchType matchType = {FontMatchType::Kind::kFontGroup, generic};
 
   for (uint32_t i = 0; i < aLength; i++) {
     const uint32_t origI = i;  // save off in case we increase for surrogate
 
     // set up current ch
     uint32_t ch = nextCh;
 
     // Get next char (if any) so that FindFontForChar can look ahead
@@ -2953,28 +2952,27 @@ void gfxFontGroup::ComputeRanges(nsTArra
     // the special cases where FindFontForChar will attempt to propagate
     // the font selected for an adjacent character.
     if ((font = GetFontAt(0, ch)) != nullptr && font->HasCharacter(ch) &&
         (sizeof(T) == sizeof(uint8_t) ||
          (!IsClusterExtender(ch) && ch != NARROW_NO_BREAK_SPACE &&
           !gfxFontUtils::IsJoinControl(ch) &&
           !gfxFontUtils::IsJoinCauser(prevCh) &&
           !gfxFontUtils::IsVarSelector(ch)))) {
-      matchType =
-          FontMatchType::kFontGroup | FontMatchType(mFonts[0].Generic());
+      matchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
     } else {
       font =
           FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont, &matchType);
     }
 
 #ifndef RELEASE_OR_BETA
     if (MOZ_UNLIKELY(mTextPerf)) {
-      if (matchType & FontMatchType::kPrefsFallback) {
+      if (matchType.kind == FontMatchType::Kind::kPrefsFallback) {
         mTextPerf->current.fallbackPrefs++;
-      } else if (matchType & FontMatchType::kSystemFallback) {
+      } else if (matchType.kind == FontMatchType::Kind::kSystemFallback) {
         mTextPerf->current.fallbackSystem++;
       }
     }
 #endif
 
     prevCh = ch;
 
     ShapedTextFlags orient = aOrientation;
@@ -3046,43 +3044,44 @@ void gfxFontGroup::ComputeRanges(nsTArra
     nsAutoCString families;
     mFamilyList.ToString(families);
 
     // collect the font matched for each range
     nsAutoCString fontMatches;
     for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
       const TextRange& r = aRanges[i];
       nsAutoCString matchTypes;
-      if (r.matchType & FontMatchType::kFontGroup) {
+      if (r.matchType.kind & FontMatchType::Kind::kFontGroup) {
         matchTypes.AppendLiteral("list");
       }
-      if (r.matchType & FontMatchType::kPrefsFallback) {
+      if (r.matchType.kind & FontMatchType::Kind::kPrefsFallback) {
         if (!matchTypes.IsEmpty()) {
           matchTypes.AppendLiteral(",");
         }
         matchTypes.AppendLiteral("prefs");
       }
-      if (r.matchType & FontMatchType::kPrefsFallback) {
+      if (r.matchType.kind & FontMatchType::Kind::kPrefsFallback) {
         if (!matchTypes.IsEmpty()) {
           matchTypes.AppendLiteral(",");
         }
         matchTypes.AppendLiteral("sys");
       }
       fontMatches.AppendPrintf(
           " [%u:%u] %.200s (%s)", r.start, r.end,
           (r.font.get() ? r.font->GetName().get() : "<null>"),
           matchTypes.get());
     }
     MOZ_LOG(log, LogLevel::Debug,
             ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
              "%s\n",
              (mStyle.systemFont ? "textrunui" : "textrun"), families.get(),
-             (mFamilyList.GetDefaultFontType() == eFamily_serif
+             (mFamilyList.GetDefaultFontType() == StyleGenericFontFamily::Serif
                   ? "serif"
-                  : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
+                  : (mFamilyList.GetDefaultFontType() ==
+                             StyleGenericFontFamily::SansSerif
                          ? "sans-serif"
                          : "none")),
              lang.get(), static_cast<int>(aRunScript), fontMatches.get()));
   }
 #endif
 }
 
 gfxUserFontSet* gfxFontGroup::GetUserFontSet() { return mUserFontSet; }
@@ -3170,18 +3169,17 @@ gfxFont* gfxFontGroup::WhichPrefFontSupp
   // based on char lang and page lang, set up list of pref lang fonts to check
   eFontPrefLang prefLangs[kMaxLenPrefLangList];
   uint32_t i, numLangs = 0;
 
   pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
 
   for (i = 0; i < numLangs; i++) {
     eFontPrefLang currentLang = prefLangs[i];
-    mozilla::FontFamilyType defaultGeneric =
-        pfl->GetDefaultGeneric(currentLang);
+    StyleGenericFontFamily defaultGeneric = pfl->GetDefaultGeneric(currentLang);
     gfxPlatformFontList::PrefFontList* families =
         pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
     NS_ASSERTION(families, "no pref font families found");
 
     // find the first pref font that includes the character
     uint32_t j, numPrefs;
     numPrefs = families->Length();
     for (j = 0; j < numPrefs; j++) {
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -846,18 +846,18 @@ class gfxFontGroup final : public gfxTex
                gfxUserFontSet* aUserFontSet, gfxFloat aDevToCssSize);
 
   virtual ~gfxFontGroup();
 
   // Returns first valid font in the fontlist or default font.
   // Initiates userfont loads if userfont not loaded.
   // aGeneric: if non-null, returns the CSS generic type that was mapped to
   //           this font
-  gfxFont* GetFirstValidFont(uint32_t aCh = 0x20,
-                             mozilla::FontFamilyType* aGeneric = nullptr);
+  gfxFont* GetFirstValidFont(
+      uint32_t aCh = 0x20, mozilla::StyleGenericFontFamily* aGeneric = nullptr);
 
   // Returns the first font in the font-group that has an OpenType MATH table,
   // or null if no such font is available. The GetMathConstant methods may be
   // called on the returned font.
   gfxFont* GetFirstMathFont();
 
   const gfxFontStyle* GetStyle() const { return &mStyle; }
 
@@ -1019,39 +1019,39 @@ class gfxFontGroup final : public gfxTex
                      uint32_t aLength, Script aRunScript,
                      mozilla::gfx::ShapedTextFlags aOrientation);
 
   class FamilyFace {
    public:
     FamilyFace()
         : mFamily(nullptr),
           mFontEntry(nullptr),
-          mGeneric(mozilla::eFamily_none),
+          mGeneric(mozilla::StyleGenericFontFamily::None),
           mFontCreated(false),
           mLoading(false),
           mInvalid(false),
           mCheckForFallbackFaces(false) {}
 
     FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont,
-               mozilla::FontFamilyType aGeneric)
+               mozilla::StyleGenericFontFamily aGeneric)
         : mFamily(aFamily),
           mGeneric(aGeneric),
           mFontCreated(true),
           mLoading(false),
           mInvalid(false),
           mCheckForFallbackFaces(false) {
       NS_ASSERTION(aFont, "font pointer must not be null");
       NS_ASSERTION(!aFamily || aFamily->ContainsFace(aFont->GetFontEntry()),
                    "font is not a member of the given family");
       mFont = aFont;
       NS_ADDREF(aFont);
     }
 
     FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
-               mozilla::FontFamilyType aGeneric)
+               mozilla::StyleGenericFontFamily aGeneric)
         : mFamily(aFamily),
           mGeneric(aGeneric),
           mFontCreated(false),
           mLoading(false),
           mInvalid(false),
           mCheckForFallbackFaces(false) {
       NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
       NS_ASSERTION(!aFamily || aFamily->ContainsFace(aFontEntry),
@@ -1110,17 +1110,17 @@ class gfxFontGroup final : public gfxTex
 
     gfxFontFamily* Family() const { return mFamily.get(); }
     gfxFont* Font() const { return mFontCreated ? mFont : nullptr; }
 
     gfxFontEntry* FontEntry() const {
       return mFontCreated ? mFont->GetFontEntry() : mFontEntry;
     }
 
-    mozilla::FontFamilyType Generic() const { return mGeneric; }
+    mozilla::StyleGenericFontFamily Generic() const { return mGeneric; }
 
     bool IsUserFontContainer() const {
       return FontEntry()->mIsUserFontContainer;
     }
     bool IsLoading() const { return mLoading; }
     bool IsInvalid() const { return mInvalid; }
     void CheckState(bool& aSkipDrawing);
     void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
@@ -1147,17 +1147,17 @@ class gfxFontGroup final : public gfxTex
     RefPtr<gfxFontFamily> mFamily;
     // either a font or a font entry exists
     union {
       // Whichever of these fields is actually present will be a strong
       // reference, with refcounting handled manually.
       gfxFont* MOZ_OWNING_REF mFont;
       gfxFontEntry* MOZ_OWNING_REF mFontEntry;
     };
-    mozilla::FontFamilyType mGeneric;
+    mozilla::StyleGenericFontFamily mGeneric;
     bool mFontCreated : 1;
     bool mLoading : 1;
     bool mInvalid : 1;
     bool mCheckForFallbackFaces : 1;
   };
 
   // List of font families, either named or generic.
   // Generic names map to system pref fonts based on language.
@@ -1256,17 +1256,17 @@ class gfxFontGroup final : public gfxTex
   // helper methods for looking up fonts
 
   // lookup and add a font with a given name (i.e. *not* a generic!)
   void AddPlatformFont(const nsACString& aName,
                        nsTArray<FamilyAndGeneric>& aFamilyList);
 
   // do style selection and add entries to list
   void AddFamilyToFontList(gfxFontFamily* aFamily,
-                           mozilla::FontFamilyType aGeneric);
+                           mozilla::StyleGenericFontFamily aGeneric);
 };
 
 // A "missing font recorder" is to be used during text-run creation to keep
 // a record of any scripts encountered for which font coverage was lacking;
 // when Flush() is called, it sends a notification that front-end code can use
 // to download fonts on demand (or whatever else it wants to do).
 
 #define GFX_MISSING_FONTS_NOTIFY_PREF "gfx.missing_fonts.notify"
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_TYPES_H
 #define GFX_TYPES_H
 
 #include <stdint.h>
 #include "mozilla/TypedEnumBits.h"
+#include "nsStyleConsts.h"
 
 typedef struct _cairo_surface cairo_surface_t;
 typedef struct _cairo_user_data_key cairo_user_data_key_t;
 
 typedef void (*thebes_destroy_func_t)(void *data);
 
 /**
  * Currently needs to be 'double' for Cairo compatibility. Could
@@ -82,24 +83,47 @@ enum class gfxAlphaType {
   Premult,
   NonPremult,
 };
 
 /**
  * Type used to record how a particular font is selected during the font-
  * matching process, so that this can be exposed to the Inspector.
  */
-enum class FontMatchType : uint16_t {
-  // The CSS generic that mapped to this font, if any. This field of
-  // the MatchType stores a FontFamilyType value as defined in the enum
-  // in gfxFontFamilyList.h.
-  kGenericMask = 0x00ff,
+struct FontMatchType {
+  enum class Kind : uint8_t {
+    kFontGroup = 1,
+    kPrefsFallback = 1 << 1,
+    kSystemFallback = 1 << 2,
+  };
+
+  inline FontMatchType& operator|=(const FontMatchType& aOther);
+
+  bool operator==(const FontMatchType& aOther) const {
+    return kind == aOther.kind && generic == aOther.generic;
+  }
 
-  // Flags for recording the kind of font-matching that was used.
-  // Note that multiple flags may be set on a single range.
-  kFontGroup = 0x0100,
-  kPrefsFallback = 0x0200,
-  kSystemFallback = 0x0400
+  bool operator!=(const FontMatchType& aOther) const {
+    return !(*this == aOther);
+  }
+
+  MOZ_IMPLICIT FontMatchType() = default;
+  MOZ_IMPLICIT FontMatchType(Kind aKind) : kind(aKind) {}
+  FontMatchType(Kind aKind, mozilla::StyleGenericFontFamily aGeneric)
+      : kind(aKind), generic(aGeneric) {}
+
+  Kind kind = static_cast<Kind>(0);
+  mozilla::StyleGenericFontFamily generic =
+      mozilla::StyleGenericFontFamily::None;
 };
 
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(FontMatchType)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(FontMatchType::Kind)
+
+FontMatchType& FontMatchType::operator|=(const FontMatchType& aOther) {
+  kind |= aOther.kind;
+  // We only keep track of one generic.
+  if (generic != aOther.generic) {
+    generic = mozilla::StyleGenericFontFamily::None;
+  }
+  return *this;
+}
 
 #endif /* GFX_TYPES_H */
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -35,17 +35,22 @@ UniquePtr<RenderCompositor> RenderCompos
 #if defined(MOZ_WAYLAND) || defined(MOZ_WIDGET_ANDROID)
   UniquePtr<RenderCompositor> eglCompositor =
       RenderCompositorEGL::Create(aWidget);
   if (eglCompositor) {
     return eglCompositor;
   }
 #endif
 
+#if defined(MOZ_WIDGET_ANDROID)
+  // RenderCompositorOGL is not used on android
+  return nullptr;
+#else
   return RenderCompositorOGL::Create(std::move(aWidget));
+#endif
 }
 
 RenderCompositor::RenderCompositor(RefPtr<widget::CompositorWidget>&& aWidget)
     : mWidget(aWidget) {}
 
 RenderCompositor::~RenderCompositor() {}
 
 bool RenderCompositor::MakeCurrent() { return gl()->MakeCurrent(); }
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -42,36 +42,18 @@ bool RenderCompositorOGL::BeginFrame() {
   }
   return true;
 }
 
 void RenderCompositorOGL::EndFrame() { mGL->SwapBuffers(); }
 
 void RenderCompositorOGL::WaitForGPU() {}
 
-void RenderCompositorOGL::Pause() {
-#ifdef MOZ_WIDGET_ANDROID
-  if (!mGL || mGL->IsDestroyed()) {
-    return;
-  }
-  // ReleaseSurface internally calls MakeCurrent.
-  mGL->ReleaseSurface();
-#endif
-}
+void RenderCompositorOGL::Pause() {}
 
-bool RenderCompositorOGL::Resume() {
-#ifdef MOZ_WIDGET_ANDROID
-  if (!mGL || mGL->IsDestroyed()) {
-    return false;
-  }
-  // RenewSurface internally calls MakeCurrent.
-  return mGL->RenewSurface(mWidget);
-#else
-  return true;
-#endif
-}
+bool RenderCompositorOGL::Resume() { return true; }
 
 LayoutDeviceIntSize RenderCompositorOGL::GetBufferSize() {
   return mWidget->GetClientSize();
 }
 
 }  // namespace wr
 }  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderEGLImageTextureHost.cpp
@@ -0,0 +1,109 @@
+/* -*- 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 "RenderEGLImageTextureHost.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderEGLImageTextureHost::RenderEGLImageTextureHost(EGLImage aImage,
+                                                     EGLSync aSync,
+                                                     gfx::IntSize aSize)
+    : mImage(aImage),
+      mSync(aSync),
+      mSize(aSize),
+      mTextureTarget(LOCAL_GL_TEXTURE_2D),
+      mTextureHandle(0) {
+  MOZ_COUNT_CTOR_INHERITED(RenderEGLImageTextureHost, RenderTextureHostOGL);
+}
+
+RenderEGLImageTextureHost::~RenderEGLImageTextureHost() {
+  MOZ_COUNT_DTOR_INHERITED(RenderEGLImageTextureHost, RenderTextureHostOGL);
+  DeleteTextureHandle();
+}
+
+GLuint RenderEGLImageTextureHost::GetGLHandle(uint8_t aChannelIndex) const {
+  return mTextureHandle;
+}
+
+gfx::IntSize RenderEGLImageTextureHost::GetSize(uint8_t aChannelIndex) const {
+  return mSize;
+}
+
+wr::WrExternalImage RenderEGLImageTextureHost::Lock(
+    uint8_t aChannelIndex, gl::GLContext* aGL, wr::ImageRendering aRendering) {
+  MOZ_ASSERT(aChannelIndex == 0);
+
+  if (mGL.get() != aGL) {
+    if (mGL) {
+      // This should not happen. SharedSurface_EGLImage is created only in
+      // parent process.
+      MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
+      return InvalidToWrExternalImage();
+    }
+    mGL = aGL;
+  }
+
+  if (!mImage || !mGL || !mGL->MakeCurrent()) {
+    return InvalidToWrExternalImage();
+  }
+
+  EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+  if (mSync) {
+    auto* egl = gl::GLLibraryEGL::Get();
+    MOZ_ASSERT(egl->IsExtensionSupported(gl::GLLibraryEGL::KHR_fence_sync));
+    status = egl->fClientWaitSync(egl->Display(), mSync, 0, LOCAL_EGL_FOREVER);
+    // We do not need to delete sync here. It is deleted by
+    // SharedSurface_EGLImage.
+    mSync = 0;
+  }
+
+  if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+    MOZ_ASSERT(
+        status != 0,
+        "ClientWaitSync generated an error. Has mSync already been destroyed?");
+    return InvalidToWrExternalImage();
+  }
+
+  if (!mTextureHandle) {
+    mTextureTarget = mGL->GetPreferredEGLImageTextureTarget();
+    MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
+               mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
+
+    mGL->fGenTextures(1, &mTextureHandle);
+    // Cache rendering filter.
+    mCachedRendering = aRendering;
+    ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, mTextureTarget,
+                                 mTextureHandle, aRendering);
+    mGL->fEGLImageTargetTexture2D(mTextureTarget, mImage);
+  } else if (IsFilterUpdateNecessary(aRendering)) {
+    // Cache new rendering filter.
+    mCachedRendering = aRendering;
+    ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, mTextureTarget,
+                                 mTextureHandle, aRendering);
+  }
+
+  return NativeTextureToWrExternalImage(mTextureHandle, 0, 0, mSize.width,
+                                        mSize.height);
+}
+
+void RenderEGLImageTextureHost::Unlock() {}
+
+void RenderEGLImageTextureHost::DeleteTextureHandle() {
+  if (mTextureHandle) {
+    // XXX recycle gl texture, since SharedSurface_EGLImage and
+    // RenderEGLImageTextureHost is not recycled.
+    mGL->fDeleteTextures(1, &mTextureHandle);
+    mTextureHandle = 0;
+  }
+}
+
+}  // namespace wr
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderEGLImageTextureHost.h
@@ -0,0 +1,46 @@
+/* -*- 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_GFX_RENDEREGLIMAGETEXTUREHOSTOGL_H
+#define MOZILLA_GFX_RENDEREGLIMAGETEXTUREHOSTOGL_H
+
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHostOGL.h"
+
+namespace mozilla {
+
+namespace wr {
+
+// RenderEGLImageTextureHost is created only for SharedSurface_EGLImage that is
+// created in parent process.
+class RenderEGLImageTextureHost final : public RenderTextureHostOGL {
+ public:
+  RenderEGLImageTextureHost(EGLImage aImage, EGLSync aSync, gfx::IntSize aSize);
+
+  wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL,
+                           wr::ImageRendering aRendering) override;
+  void Unlock() override;
+
+  virtual gfx::IntSize GetSize(uint8_t aChannelIndex) const override;
+  virtual GLuint GetGLHandle(uint8_t aChannelIndex) const override;
+
+ private:
+  virtual ~RenderEGLImageTextureHost();
+  void DeleteTextureHandle();
+
+  const EGLImage mImage;
+  EGLSync mSync;
+  const gfx::IntSize mSize;
+
+  RefPtr<gl::GLContext> mGL;
+  GLenum mTextureTarget;
+  GLuint mTextureHandle;
+};
+
+}  // namespace wr
+}  // namespace mozilla
+
+#endif  // MOZILLA_GFX_RENDEREGLIMAGETEXTUREHOSTOGL_H
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -6,16 +6,17 @@
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Graphics: WebRender')
 
 EXPORTS.mozilla.webrender += [
     'RenderBufferTextureHost.h',
     'RenderCompositor.h',
     'RenderCompositorOGL.h',
+    'RenderEGLImageTextureHost.h',
     'RendererOGL.h',
     'RendererScreenshotGrabber.h',
     'RenderSharedSurfaceTextureHost.h',
     'RenderTextureHost.h',
     'RenderTextureHostOGL.h',
     'RenderTextureHostWrapper.h',
     'RenderThread.h',
     'webrender_ffi.h',
@@ -23,16 +24,17 @@ EXPORTS.mozilla.webrender += [
     'WebRenderTypes.h',
 ]
 
 UNIFIED_SOURCES += [
     'Moz2DImageRenderer.cpp',
     'RenderBufferTextureHost.cpp',
     'RenderCompositor.cpp',
     'RenderCompositorOGL.cpp',
+    'RenderEGLImageTextureHost.cpp',
     'RendererOGL.cpp',
     'RendererScreenshotGrabber.cpp',
     'RenderSharedSurfaceTextureHost.cpp',
     'RenderTextureHost.cpp',
     'RenderTextureHostOGL.cpp',
     'RenderTextureHostWrapper.cpp',
     'RenderThread.cpp',
     'WebRenderAPI.cpp',
--- a/image/test/mochitest/test_discardAnimatedImage.html
+++ b/image/test/mochitest/test_discardAnimatedImage.html
@@ -109,32 +109,34 @@ function step4() {
 
 function checkIfFinished() {
   if (gFinished) {
     return;
   }
 
   if ((gNumFrameUpdates[0] >= kNumFrameUpdatesToExpect) &&
       (gNumFrameUpdates[1] >= kNumFrameUpdatesToExpect) &&
+      (gNumFrameUpdates[2] >= kNumFrameUpdatesToExpect) &&
       (gNumSnapShotChanges[0] >= kNumFrameUpdatesToExpect) &&
-      (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect)) {
+      (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect) &&
+      (gNumSnapShotChanges[2] >= kNumFrameUpdatesToExpect)) {
     ok(true, "got expected frame updates");
     gFinished = true;
     SimpleTest.finish();
   }
 }
 
 // arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes
 // to increment when a frame update notification is received.
 function addCallbacks(anImage, arrayIndex) {
   var observer = new ImageDecoderObserverStub();
   observer.discard = function () {
     gNumDiscards++;
     ok(true, "got image discard");
-    if (arrayIndex >= 2) {
+    if (arrayIndex >= 3) {
       // The last two images are finite, so we don't expect any frame updates,
       // this image is done the test, so remove the observer.
       if (!gRemovedObserver[arrayIndex]) {
         gRemovedObserver[arrayIndex] = true;
         imgLoadingContent.removeObserver(scriptedObserver);
       }
     }
     if (gNumDiscards == gImgs.length) {
--- a/js/ipc/JavaScriptParent.cpp
+++ b/js/ipc/JavaScriptParent.cpp
@@ -93,17 +93,18 @@ bool JavaScriptParent::allowMessage(JSCo
     if (console) {
       nsAutoString filename;
       uint32_t lineno = 0, column = 0;
       nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
       nsCOMPtr<nsIScriptError> error(
           do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
       error->Init(NS_LITERAL_STRING("unsafe/forbidden CPOW usage"), filename,
                   EmptyString(), lineno, column, nsIScriptError::warningFlag,
-                  "chrome javascript", false /* from private window */);
+                  "chrome javascript", false /* from private window */,
+                  true /* from chrome context */);
       console->LogMessage(error);
     } else {
       NS_WARNING("Unsafe synchronous IPC message");
     }
   }
 
   return true;
 }
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -767,17 +767,17 @@ bool WrapperAnswer::RecvRegExpToShared(c
     return fail(jsapi, rs);
   }
   nsAutoJSString sourceStr;
   if (!sourceStr.init(cx, sourceJSStr)) {
     return fail(jsapi, rs);
   }
   source->Assign(sourceStr);
 
-  *flags = JS::GetRegExpFlags(cx, obj);
+  *flags = JS::GetRegExpFlags(cx, obj).value();
 
   return ok(rs);
 }
 
 bool WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId,
                                         const uint32_t& flags, ReturnStatus* rs,
                                         nsTArray<JSIDVariant>* ids) {
   if (!IsInAutomation()) {
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -7,16 +7,17 @@
 
 #include "WrapperOwner.h"
 #include "JavaScriptLogging.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "js/CharacterEncoding.h"
 #include "js/RegExp.h"
+#include "js/RegExpFlags.h"
 #include "xpcprivate.h"
 #include "WrapperFactory.h"
 
 #include "nsIDocShellTreeItem.h"
 #include "mozilla/dom/Document.h"
 
 using namespace js;
 using namespace JS;
@@ -828,17 +829,18 @@ RegExpShared* WrapperOwner::regexp_toSha
   }
   LOG_STACK();
 
   if (!ok(cx, status)) {
     return nullptr;
   }
 
   RootedObject regexp(cx);
-  regexp = JS::NewUCRegExpObject(cx, source.get(), source.Length(), flags);
+  regexp = JS::NewUCRegExpObject(cx, source.get(), source.Length(),
+                                 RegExpFlags(flags));
   if (!regexp) {
     return nullptr;
   }
 
   return js::RegExpToSharedNonInline(cx, regexp);
 }
 
 void CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const {
--- a/js/public/RegExp.h
+++ b/js/public/RegExp.h
@@ -8,64 +8,39 @@
 
 #ifndef js_RegExp_h
 #define js_RegExp_h
 
 #include <stddef.h>  // size_t
 
 #include "jstypes.h"  // JS_PUBLIC_API
 
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/RootingAPI.h"  // JS::{,Mutable}Handle
 #include "js/Value.h"       // JS::Value
 
 struct JSContext;
 class JSString;
 
 namespace JS {
 
 /**
- * A namespace for all regular expression flags as they appear in the APIs below
- * as flags values.
- */
-struct RegExpFlags {
- public:
-  /**
-   * Interpret regular expression source text case-insensitively by folding
-   * uppercase letters to lowercase, i.e. /i.
-   */
-  static constexpr unsigned IgnoreCase = 0b0'0001;
-
-  /**
-   * Act globally and find *all* matches (rather than stopping after just the
-   * first one), i.e. /g.
-   */
-  static constexpr unsigned Global = 0b0'0010;
-
-  /** Treat ^ and $ as begin and end of line, i.e. /m. */
-  static constexpr unsigned Multiline = 0b0'0100;
-
-  /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
-  static constexpr unsigned Sticky = 0b0'1000;
-
-  /** Use Unicode semantics, i.e. /u. */
-  static constexpr unsigned Unicode = 0b1'0000;
-};
-
-/**
  * Create a new RegExp for the given Latin-1-encoded bytes and flags.
  */
 extern JS_PUBLIC_API JSObject* NewRegExpObject(JSContext* cx, const char* bytes,
-                                               size_t length, unsigned flags);
+                                               size_t length,
+                                               RegExpFlags flags);
 
 /**
  * Create a new RegExp for the given source and flags.
  */
 extern JS_PUBLIC_API JSObject* NewUCRegExpObject(JSContext* cx,
                                                  const char16_t* chars,
-                                                 size_t length, unsigned flags);
+                                                 size_t length,
+                                                 RegExpFlags flags);
 
 extern JS_PUBLIC_API bool SetRegExpInput(JSContext* cx, Handle<JSObject*> obj,
                                          Handle<JSString*> input);
 
 extern JS_PUBLIC_API bool ClearRegExpStatics(JSContext* cx,
                                              Handle<JSObject*> obj);
 
 extern JS_PUBLIC_API bool ExecuteRegExp(JSContext* cx, Handle<JSObject*> obj,
@@ -90,20 +65,20 @@ extern JS_PUBLIC_API bool ExecuteRegExpN
  * This method returns true with |*isRegExp == false| when passed an ES6 proxy
  * whose target is a RegExp, or when passed a revoked proxy.
  */
 extern JS_PUBLIC_API bool ObjectIsRegExp(JSContext* cx, Handle<JSObject*> obj,
                                          bool* isRegExp);
 
 /**
  * Given a RegExp object (or a wrapper around one), return the set of all
- * JS::RegExpFlags::* for it.
+ * JS::RegExpFlag::* for it.
  */
-extern JS_PUBLIC_API unsigned GetRegExpFlags(JSContext* cx,
-                                             Handle<JSObject*> obj);
+extern JS_PUBLIC_API RegExpFlags GetRegExpFlags(JSContext* cx,
+                                                Handle<JSObject*> obj);
 
 /**
  * Return the source text for a RegExp object (or a wrapper around one), or null
  * on failure.
  */
 extern JS_PUBLIC_API JSString* GetRegExpSource(JSContext* cx,
                                                Handle<JSObject*> obj);
 
copy from js/public/RegExp.h
copy to js/public/RegExpFlags.h
--- a/js/public/RegExp.h
+++ b/js/public/RegExpFlags.h
@@ -1,112 +1,124 @@
 /* -*- 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/. */
 
-/* Regular expression-related operations. */
+/* Regular expression flags. */
 
-#ifndef js_RegExp_h
-#define js_RegExp_h
-
-#include <stddef.h>  // size_t
+#ifndef js_RegExpFlags_h
+#define js_RegExpFlags_h
 
-#include "jstypes.h"  // JS_PUBLIC_API
+#include "mozilla/Assertions.h"  // MOZ_ASSERT
+#include "mozilla/Attributes.h"  // MOZ_IMPLICIT
 
-#include "js/RootingAPI.h"  // JS::{,Mutable}Handle
-#include "js/Value.h"       // JS::Value
-
-struct JSContext;
-class JSString;
+#include <stdint.h>  // uint8_t
 
 namespace JS {
 
 /**
- * A namespace for all regular expression flags as they appear in the APIs below
- * as flags values.
+ * Regular expression flag values, suitable for initializing a collection of
+ * regular expression flags as defined below in |RegExpFlags|.  Flags are listed
+ * in alphabetical order by syntax -- /g, /i, /m, /u, /y.
  */
-struct RegExpFlags {
+class RegExpFlag {
+  // WARNING TO SPIDERMONKEY HACKERS (embedders must assume these values can
+  // change):
+  //
+  // Flag-bit values appear in XDR and structured clone data formats, so none of
+  // these values can be changed (including to assign values in numerically
+  // ascending order) unless you also add a translation layer.
+
  public:
   /**
-   * Interpret regular expression source text case-insensitively by folding
-   * uppercase letters to lowercase, i.e. /i.
-   */
-  static constexpr unsigned IgnoreCase = 0b0'0001;
-
-  /**
    * Act globally and find *all* matches (rather than stopping after just the
    * first one), i.e. /g.
    */
-  static constexpr unsigned Global = 0b0'0010;
+  static constexpr uint8_t Global = 0b0'0010;
+
+  /**
+   * Interpret regular expression source text case-insensitively by folding
+   * uppercase letters to lowercase, i.e. /i.
+   */
+  static constexpr uint8_t IgnoreCase = 0b0'0001;
 
   /** Treat ^ and $ as begin and end of line, i.e. /m. */
-  static constexpr unsigned Multiline = 0b0'0100;
+  static constexpr uint8_t Multiline = 0b0'0100;
+
+  /** Use Unicode semantics, i.e. /u. */
+  static constexpr uint8_t Unicode = 0b1'0000;
 
   /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
-  static constexpr unsigned Sticky = 0b0'1000;
+  static constexpr uint8_t Sticky = 0b0'1000;
 
-  /** Use Unicode semantics, i.e. /u. */
-  static constexpr unsigned Unicode = 0b1'0000;
+  /** No regular expression flags. */
+  static constexpr uint8_t NoFlags = 0b0'0000;
+
+  /** All regular expression flags. */
+  static constexpr uint8_t AllFlags = 0b1'1111;
 };
 
 /**
- * Create a new RegExp for the given Latin-1-encoded bytes and flags.
+ * A collection of regular expression flags.  Individual flag values may be
+ * combined into a collection using bitwise operators.
  */
-extern JS_PUBLIC_API JSObject* NewRegExpObject(JSContext* cx, const char* bytes,
-                                               size_t length, unsigned flags);
+class RegExpFlags {
+ public:
+  using Flag = uint8_t;
 
-/**
- * Create a new RegExp for the given source and flags.
- */
-extern JS_PUBLIC_API JSObject* NewUCRegExpObject(JSContext* cx,
-                                                 const char16_t* chars,
-                                                 size_t length, unsigned flags);
+ private:
+  Flag flags_;
+
+ public:
+  RegExpFlags() = default;
 
-extern JS_PUBLIC_API bool SetRegExpInput(JSContext* cx, Handle<JSObject*> obj,
-                                         Handle<JSString*> input);
+  MOZ_IMPLICIT RegExpFlags(Flag flags) : flags_(flags) {
+    MOZ_ASSERT((flags & RegExpFlag::AllFlags) == flags,
+               "flags must not contain unrecognized flags");
+  }
 
-extern JS_PUBLIC_API bool ClearRegExpStatics(JSContext* cx,
-                                             Handle<JSObject*> obj);
+  RegExpFlags(const RegExpFlags&) = default;
 
-extern JS_PUBLIC_API bool ExecuteRegExp(JSContext* cx, Handle<JSObject*> obj,
-                                        Handle<JSObject*> reobj,
-                                        char16_t* chars, size_t length,
-                                        size_t* indexp, bool test,
-                                        MutableHandle<Value> rval);
+  bool operator==(const RegExpFlags& other) const {
+    return flags_ == other.flags_;
+  }
 
-/* RegExp interface for clients without a global object. */
+  bool operator!=(const RegExpFlags& other) const { return !(*this == other); }
+
+  RegExpFlags operator&(Flag flag) const { return RegExpFlags(flags_ & flag); }
+
+  RegExpFlags operator|(Flag flag) const { return RegExpFlags(flags_ | flag); }
 
-extern JS_PUBLIC_API bool ExecuteRegExpNoStatics(JSContext* cx,
-                                                 Handle<JSObject*> reobj,
-                                                 char16_t* chars, size_t length,
-                                                 size_t* indexp, bool test,
-                                                 MutableHandle<Value> rval);
+  RegExpFlags operator^(Flag flag) const { return RegExpFlags(flags_ ^ flag); }
+
+  RegExpFlags operator~() const { return RegExpFlags(~flags_); }
+
+  bool global() const { return flags_ & RegExpFlag::Global; }
+  bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
+  bool multiline() const { return flags_ & RegExpFlag::Multiline; }
+  bool unicode() const { return flags_ & RegExpFlag::Unicode; }
+  bool sticky() const { return flags_ & RegExpFlag::Sticky; }
+
+  explicit operator bool() const { return flags_ != 0; }
+
+  Flag value() const { return flags_; }
+};
 
-/**
- * On success, returns true, setting |*isRegExp| to true if |obj| is a RegExp
- * object or a wrapper around one, or to false if not.  Returns false on
- * failure.
- *
- * This method returns true with |*isRegExp == false| when passed an ES6 proxy
- * whose target is a RegExp, or when passed a revoked proxy.
- */
-extern JS_PUBLIC_API bool ObjectIsRegExp(JSContext* cx, Handle<JSObject*> obj,
-                                         bool* isRegExp);
+inline RegExpFlags& operator&=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags & flag;
+  return flags;
+}
 
-/**
- * Given a RegExp object (or a wrapper around one), return the set of all
- * JS::RegExpFlags::* for it.
- */
-extern JS_PUBLIC_API unsigned GetRegExpFlags(JSContext* cx,
-                                             Handle<JSObject*> obj);
+inline RegExpFlags& operator|=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags | flag;
+  return flags;
+}
 
-/**
- * Return the source text for a RegExp object (or a wrapper around one), or null
- * on failure.
- */
-extern JS_PUBLIC_API JSString* GetRegExpSource(JSContext* cx,
-                                               Handle<JSObject*> obj);
+inline RegExpFlags& operator^=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags ^ flag;
+  return flags;
+}
 
 }  // namespace JS
 
-#endif  // js_RegExp_h
+#endif  // js_RegExpFlags_h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1,40 +1,45 @@
 /* -*- 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 "builtin/RegExp.h"
 
+#include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
 #include "js/PropertySpec.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/ObjectOperations-inl.h"
 
 using namespace js;
 
+using mozilla::AssertedCast;
 using mozilla::CheckedInt;
 using mozilla::IsAsciiDigit;
 
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 16-25.
  */
 bool js::CreateRegExpMatchResult(JSContext* cx, HandleString input,
                                  const MatchPairs& matches,
                                  MutableHandleValue rval) {
@@ -182,26 +187,26 @@ bool js::ExecuteRegExpLegacy(JSContext* 
     rval.setBoolean(true);
     return true;
   }
 
   return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
 static bool CheckPatternSyntaxSlow(JSContext* cx, HandleAtom pattern,
-                                   RegExpFlag flags) {
+                                   RegExpFlags flags) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
   return irregexp::ParsePatternSyntax(dummyTokenStream, allocScope.alloc(),
-                                      pattern, flags & UnicodeFlag);
+                                      pattern, flags.unicode());
 }
 
 static RegExpShared* CheckPatternSyntax(JSContext* cx, HandleAtom pattern,
-                                        RegExpFlag flags) {
+                                        RegExpFlags flags) {
   // If we already have a RegExpShared for this pattern/flags, we can
   // avoid the much slower CheckPatternSyntaxSlow call.
 
   if (RegExpShared* shared = cx->zone()->regExps().maybeGet(pattern, flags)) {
 #ifdef DEBUG
     // Assert the pattern is valid.
     if (!CheckPatternSyntaxSlow(cx, pattern, flags)) {
       MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
@@ -247,17 +252,17 @@ static bool RegExpInitializeIgnoringLast
     /* Step 2. */
     pattern = ToAtom<CanGC>(cx, patternValue);
     if (!pattern) {
       return false;
     }
   }
 
   /* Step 3. */
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (!flagsValue.isUndefined()) {
     /* Step 4. */
     RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
     if (!flagStr) {
       return false;
     }
 
     /* Step 5. */
@@ -358,17 +363,17 @@ MOZ_ALWAYS_INLINE bool regexp_compile_im
     }
 
     // Beware!  |patternObj| might be a proxy into another compartment, so
     // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
     // don't reuse the RegExpShared below.
     RootedObject patternObj(cx, &patternValue.toObject());
 
     RootedAtom sourceAtom(cx);
-    RegExpFlag flags;
+    RegExpFlags flags = RegExpFlag::NoFlags;
     {
       // Step 3b.
       RegExpShared* shared = RegExpToShared(cx, patternObj);
       if (!shared) {
         return false;
       }
 
       sourceAtom = shared->getSource();
@@ -456,17 +461,17 @@ bool js::regexp_construct(JSContext* cx,
     return false;
   }
   if (cls == ESClass::RegExp) {
     // Beware!  |patternObj| might be a proxy into another compartment, so
     // don't assume |patternObj.is<RegExpObject>()|.
     RootedObject patternObj(cx, &patternValue.toObject());
 
     RootedAtom sourceAtom(cx);
-    RegExpFlag flags;
+    RegExpFlags flags;
     RootedRegExpShared shared(cx);
     {
       // Step 4.a.
       shared = RegExpToShared(cx, patternObj);
       if (!shared) {
         return false;
       }
       sourceAtom = shared->getSource();
@@ -490,31 +495,31 @@ bool js::regexp_construct(JSContext* cx,
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));
     if (!regexp) {
       return false;
     }
 
     // Step 8.
     if (args.hasDefined(1)) {
       // Step 4.c / 21.2.3.2.2 RegExpInitialize step 4.
-      RegExpFlag flagsArg = RegExpFlag(0);
+      RegExpFlags flagsArg = RegExpFlag::NoFlags;
       RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
       if (!flagStr) {
         return false;
       }
       if (!ParseRegExpFlags(cx, flagStr, &flagsArg)) {
         return false;
       }
 
       // Don't reuse the RegExpShared if we have different flags.
       if (flags != flagsArg) {
         shared = nullptr;
       }
 
-      if (!(flags & UnicodeFlag) && flagsArg & UnicodeFlag) {
+      if (!flags.unicode() && flagsArg.unicode()) {
         // Have to check syntax again when adding 'u' flag.
 
         // ES 2017 draft rev 9b49a888e9dfe2667008a01b2754c3662059ae56
         // 21.2.3.2.2 step 7.
         shared = CheckPatternSyntax(cx, sourceAtom, flagsArg);
         if (!shared) {
           return false;
         }
@@ -589,26 +594,26 @@ bool js::regexp_construct_raw_flags(JSCo
 
   // Step 4.a.
   RootedAtom sourceAtom(cx, AtomizeString(cx, args[0].toString()));
   if (!sourceAtom) {
     return false;
   }
 
   // Step 4.c.
-  int32_t flags = int32_t(args[1].toNumber());
+  RegExpFlags flags = AssertedCast<uint8_t>(int32_t(args[1].toNumber()));
 
   // Step 7.
   RegExpObject* regexp = RegExpAlloc(cx, GenericObject);
   if (!regexp) {
     return false;
   }
 
   // Step 8.
-  regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
+  regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
   args.rval().setObject(*regexp);
   return true;
 }
 
 MOZ_ALWAYS_INLINE bool IsRegExpPrototype(HandleValue v, JSContext* cx) {
   return (v.isObject() &&
           cx->global()->maybeGetRegExpPrototype() == &v.toObject());
 }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -44,16 +44,17 @@
 #include "js/ArrayBuffer.h"  // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/LocaleSensitive.h"
 #include "js/PropertySpec.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
@@ -92,16 +93,18 @@
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
 using JS::AutoStableStringChars;
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using JS::SourceOwnership;
 using JS::SourceText;
 
 // If fuzzingSafe is set, remove functionality that could cause problems with
 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
 mozilla::Atomic<bool> fuzzingSafe(false);
 
 // If disableOOMFunctions is set, disable functionality that causes artificial
@@ -4847,17 +4850,17 @@ static bool ParseRegExp(JSContext* cx, u
     return false;
   }
 
   if (!args[0].isString()) {
     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
     return false;
   }
 
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (!args.get(1).isUndefined()) {
     if (!args.get(1).isString()) {
       ReportUsageErrorASCII(cx, callee,
                             "Second argument, if present, must be a String");
       return false;
     }
     RootedString flagStr(cx, args[1].toString());
     if (!ParseRegExpFlags(cx, flagStr, &flags)) {
@@ -4882,19 +4885,17 @@ static bool ParseRegExp(JSContext* cx, u
 
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
   // Data lifetime is controlled by LifoAllocScope.
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   irregexp::RegExpCompileData data;
   if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
-                              flags & MultilineFlag, match_only,
-                              flags & UnicodeFlag, flags & IgnoreCaseFlag,
-                              flags & GlobalFlag, flags & StickyFlag, &data)) {
+                              match_only, flags, &data)) {
     return false;
   }
 
   RootedObject obj(
       cx, ConvertRegExpTreeToObject(cx, allocScope.alloc(), data.tree));
   if (!obj) {
     return false;
   }
--- a/js/src/frontend/BinAST.yaml
+++ b/js/src/frontend/BinAST.yaml
@@ -39,21 +39,24 @@ cpp:
     #include "mozilla/Vector.h"
 
     #include "frontend/BinAST-macros.h"
     #include "frontend/BinASTTokenReaderMultipart.h"
     #include "frontend/FullParseHandler.h"
     #include "frontend/ParseNode.h"
     #include "frontend/Parser.h"
     #include "frontend/SharedContext.h"
-
+    #include "js/RegExpFlags.h"  //  JS::RegExpFlag, JS::RegExpFlags
     #include "vm/RegExpObject.h"
 
     #include "frontend/ParseContext-inl.h"
 
+    using JS::RegExpFlag;
+    using JS::RegExpFlags;
+
     namespace js {
     namespace frontend {
 
     // Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
     // a string literal (and ONLY a string literal).
     template<typename Tok, size_t N>
     bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
       return Tok::equals(left, right);
@@ -1276,28 +1279,28 @@ LiteralPropertyName:
 LiteralRegExpExpression:
   fields:
     flags:
       block:
         replace: |
           Chars flags(cx_);
           MOZ_TRY(tokenizer_->readChars(flags));
   build: |
-    RegExpFlag reflags = NoFlags;
+    RegExpFlags reflags = RegExpFlag::NoFlags;
     for (auto c : flags) {
-      if (c == 'g' && !(reflags & GlobalFlag)) {
-        reflags = RegExpFlag(reflags | GlobalFlag);
-      } else if (c == 'i' && !(reflags & IgnoreCaseFlag)) {
-        reflags = RegExpFlag(reflags | IgnoreCaseFlag);
-      } else if (c == 'm' && !(reflags & MultilineFlag)) {
-        reflags = RegExpFlag(reflags | MultilineFlag);
-      } else if (c == 'y' && !(reflags & StickyFlag)) {
-        reflags = RegExpFlag(reflags | StickyFlag);
-      } else if (c == 'u' && !(reflags & UnicodeFlag)) {
-        reflags = RegExpFlag(reflags | UnicodeFlag);
+      if (c == 'g' && !reflags.global()) {
+        reflags |= RegExpFlag::Global;
+      } else if (c == 'i' && !reflags.ignoreCase()) {
+        reflags |= RegExpFlag::IgnoreCase;
+      } else if (c == 'm' && !reflags.multiline()) {
+        reflags |= RegExpFlag::Multiline;
+      } else if (c == 'u' && !reflags.unicode()) {
+        reflags |= RegExpFlag::Unicode;
+      } else if (c == 'y' && !reflags.sticky()) {
+        reflags |= RegExpFlag::Sticky;
       } else {
         return raiseError("Invalid regexp flags");
       }
     }
 
 
     Rooted<RegExpObject*> reobj(cx_);
     BINJS_TRY_VAR(reobj,
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -19,21 +19,24 @@
 #include "mozilla/Vector.h"
 
 #include "frontend/BinAST-macros.h"
 #include "frontend/BinASTTokenReaderMultipart.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
-
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "vm/RegExpObject.h"
 
 #include "frontend/ParseContext-inl.h"
 
+using JS::RegExpFlag;
+using JS::RegExpFlags;
+
 namespace js {
 namespace frontend {
 
 // Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
 // a string literal (and ONLY a string literal).
 template <typename Tok, size_t N>
 bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
   return Tok::equals(left, right);
@@ -3517,28 +3520,28 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom pattern(cx_);
   MOZ_TRY_VAR(pattern, tokenizer_->readAtom());
   Chars flags(cx_);
   MOZ_TRY(tokenizer_->readChars(flags));
 
-  RegExpFlag reflags = NoFlags;
+  RegExpFlags reflags = RegExpFlag::NoFlags;
   for (auto c : flags) {
-    if (c == 'g' && !(reflags & GlobalFlag)) {
-      reflags = RegExpFlag(reflags | GlobalFlag);
-    } else if (c == 'i' && !(reflags & IgnoreCaseFlag)) {
-      reflags = RegExpFlag(reflags | IgnoreCaseFlag);
-    } else if (c == 'm' && !(reflags & MultilineFlag)) {
-      reflags = RegExpFlag(reflags | MultilineFlag);
-    } else if (c == 'y' && !(reflags & StickyFlag)) {
-      reflags = RegExpFlag(reflags | StickyFlag);
-    } else if (c == 'u' && !(reflags & UnicodeFlag)) {
-      reflags = RegExpFlag(reflags | UnicodeFlag);
+    if (c == 'g' && !reflags.global()) {
+      reflags |= RegExpFlag::Global;
+    } else if (c == 'i' && !reflags.ignoreCase()) {
+      reflags |= RegExpFlag::IgnoreCase;
+    } else if (c == 'm' && !reflags.multiline()) {
+      reflags |= RegExpFlag::Multiline;
+    } else if (c == 'u' && !reflags.unicode()) {
+      reflags |= RegExpFlag::Unicode;
+    } else if (c == 'y' && !reflags.sticky()) {
+      reflags |= RegExpFlag::Sticky;
     } else {
       return raiseError("Invalid regexp flags");
     }
   }
 
   Rooted<RegExpObject*> reobj(cx_);
   BINJS_TRY_VAR(reobj,
                 RegExpObject::create(cx_, pattern, reflags, TenuredObject));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -37,16 +37,17 @@
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/ModuleSharedContext.h"
 #include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
 #include "vm/StringType.h"
 #include "wasm/AsmJS.h"
@@ -65,16 +66,17 @@ using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::PointerRangeSize;
 using mozilla::Some;
 using mozilla::Unused;
 using mozilla::Utf8Unit;
 
 using JS::AutoGCRooter;
 using JS::ReadOnlyCompileOptions;
+using JS::RegExpFlags;
 
 namespace js {
 namespace frontend {
 
 using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
 using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
 using BindingIter = ParseContext::Scope::BindingIter;
 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
@@ -9252,17 +9254,17 @@ GeneralParser<ParseHandler, Unit>::noSub
 }
 
 template <typename Unit>
 RegExpLiteral* Parser<FullParseHandler, Unit>::newRegExp() {
   MOZ_ASSERT(!options().selfHostingMode);
 
   // Create the regexp and check its syntax.
   const auto& chars = tokenStream.getCharBuffer();
-  RegExpFlag flags = anyChars.currentToken().regExpFlags();
+  RegExpFlags flags = anyChars.currentToken().regExpFlags();
 
   Rooted<RegExpObject*> reobj(cx_);
   reobj = RegExpObject::create(cx_, chars.begin(), chars.length(), flags,
                                anyChars, TenuredObject);
   if (!reobj) {
     return null();
   }
 
@@ -9271,23 +9273,23 @@ RegExpLiteral* Parser<FullParseHandler, 
 
 template <typename Unit>
 SyntaxParseHandler::RegExpLiteralType
 Parser<SyntaxParseHandler, Unit>::newRegExp() {
   MOZ_ASSERT(!options().selfHostingMode);
 
   // Only check the regexp's syntax, but don't create a regexp object.
   const auto& chars = tokenStream.getCharBuffer();
-  RegExpFlag flags = anyChars.currentToken().regExpFlags();
+  RegExpFlags flags = anyChars.currentToken().regExpFlags();
 
   mozilla::Range<const char16_t> source(chars.begin(), chars.length());
   {
     LifoAllocScope scopeAlloc(&alloc_);
     if (!js::irregexp::ParsePatternSyntax(anyChars, scopeAlloc.alloc(), source,
-                                          flags & UnicodeFlag)) {
+                                          flags.unicode())) {
       return null();
     }
   }
 
   return handler_.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
 }
 
 template <class ParseHandler, typename Unit>
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -29,16 +29,17 @@
 
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/Parser.h"
 #include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/UniquePtr.h"
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 
@@ -51,16 +52,18 @@ using mozilla::IsAsciiDigit;
 using mozilla::IsTrailingUnit;
 using mozilla::MakeScopeExit;
 using mozilla::MakeSpan;
 using mozilla::Maybe;
 using mozilla::PointerRangeSize;
 using mozilla::Utf8Unit;
 
 using JS::ReadOnlyCompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 
 struct ReservedWordInfo {
   const char* chars;  // C string with reserved word text
   js::frontend::TokenKind tokentype;
 };
 
 static const ReservedWordInfo reservedWords[] = {
 #define RESERVED_WORD_INFO(word, name, type) \
@@ -2314,44 +2317,44 @@ MOZ_MUST_USE bool TokenStreamSpecific<Un
 
     MOZ_ASSERT(!IsLineTerminator(AssertedCast<char32_t>(unit)));
     if (!this->charBuffer.append(unit)) {
       return badToken();
     }
   } while (true);
 
   int32_t unit;
-  RegExpFlag reflags = NoFlags;
+  RegExpFlags reflags = RegExpFlag::NoFlags;
   while (true) {
-    RegExpFlag flag;
+    uint8_t flag;
     unit = getCodeUnit();
     if (unit == 'g') {
-      flag = GlobalFlag;
+      flag = RegExpFlag::Global;
     } else if (unit == 'i') {
-      flag = IgnoreCaseFlag;
+      flag = RegExpFlag::IgnoreCase;
     } else if (unit == 'm') {
-      flag = MultilineFlag;
+      flag = RegExpFlag::Multiline;
+    } else if (unit == 'u') {
+      flag = RegExpFlag::Unicode;
     } else if (unit == 'y') {
-      flag = StickyFlag;
-    } else if (unit == 'u') {
-      flag = UnicodeFlag;
+      flag = RegExpFlag::Sticky;
     } else if (IsAsciiAlpha(unit)) {
-      flag = NoFlags;
+      flag = RegExpFlag::NoFlags;
     } else {
       break;
     }
 
-    if ((reflags & flag) || flag == NoFlags) {
+    if ((reflags & flag) || flag == RegExpFlag::NoFlags) {
       ungetCodeUnit(unit);
       char buf[2] = {char(unit), '\0'};
       error(JSMSG_BAD_REGEXP_FLAG, buf);
       return badToken();
     }
 
-    reflags = RegExpFlag(reflags | flag);
+    reflags |= flag;
   }
   ungetCodeUnit(unit);
 
   newRegExpToken(reflags, start, out);
   return true;
 }
 
 template <typename Unit, class AnyCharsAccess>
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -201,23 +201,23 @@
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jspubtd.h"
 
 #include "frontend/ErrorReporter.h"
 #include "frontend/TokenKind.h"
 #include "js/CompileOptions.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "util/Text.h"
 #include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 #include "vm/JSAtom.h"
-#include "vm/RegExpConstants.h"
 #include "vm/StringType.h"
 
 struct JSContext;
 struct KeywordInfo;
 
 namespace js {
 
 class AutoKeepAtoms;
@@ -375,17 +375,17 @@ struct Token {
       /** Numeric literal's value. */
       double value;
 
       /** Does the numeric literal contain a '.'? */
       DecimalPoint decimalPoint;
     } number;
 
     /** Regular expression flags; use charBuffer to access source chars. */
-    RegExpFlag reflags;
+    JS::RegExpFlags reflags;
   } u;
 
 #ifdef DEBUG
   /** The modifier used to get this token. */
   Modifier modifier;
 
   /**
    * Exception for this modifier to permit modifier mismatches in certain
@@ -402,19 +402,18 @@ struct Token {
   }
 
   void setAtom(JSAtom* atom) {
     MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead ||
                type == TokenKind::NoSubsTemplate);
     u.atom = atom;
   }
 
-  void setRegExpFlags(RegExpFlag flags) {
+  void setRegExpFlags(JS::RegExpFlags flags) {
     MOZ_ASSERT(type == TokenKind::RegExp);
-    MOZ_ASSERT((flags & AllFlags) == flags);
     u.reflags = flags;
   }
 
   void setNumber(double n, DecimalPoint decimalPoint) {
     MOZ_ASSERT(type == TokenKind::Number);
     u.number.value = n;
     u.number.decimalPoint = decimalPoint;
   }
@@ -427,19 +426,18 @@ struct Token {
   }
 
   JSAtom* atom() const {
     MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead ||
                type == TokenKind::NoSubsTemplate);
     return u.atom;
   }
 
-  RegExpFlag regExpFlags() const {
+  JS::RegExpFlags regExpFlags() const {
     MOZ_ASSERT(type == TokenKind::RegExp);
-    MOZ_ASSERT((u.reflags & AllFlags) == u.reflags);
     return u.reflags;
   }
 
   double number() const {
     MOZ_ASSERT(type == TokenKind::Number);
     return u.number.value;
   }
 
@@ -1994,17 +1992,18 @@ class GeneralTokenStreamChars : public S
 
   void newPrivateNameToken(PropertyName* name, TokenStart start,
                            TokenStreamShared::Modifier modifier,
                            TokenKind* out) {
     Token* token = newToken(TokenKind::PrivateName, start, modifier, out);
     token->setName(name);
   }
 
-  void newRegExpToken(RegExpFlag reflags, TokenStart start, TokenKind* out) {
+  void newRegExpToken(JS::RegExpFlags reflags, TokenStart start,
+                      TokenKind* out) {
     Token* token =
         newToken(TokenKind::RegExp, start, TokenStreamShared::Operand, out);
     token->setRegExpFlags(reflags);
   }
 
   MOZ_COLD bool badToken();
 
   /**
@@ -2797,17 +2796,18 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
 // (Unit, AnyCharsAccess) pairs -- and that gets super-messy as AnyCharsAccess
 // *itself* is templated.  This symbol really isn't that huge compared to some
 // defined inline in TokenStreamSpecific, so just rely on the linker commoning
 // stuff up.
 template <typename Unit>
 template <class AnyCharsAccess>
 inline TokenStreamPosition<Unit>::TokenStreamPosition(
     AutoKeepAtoms& keepAtoms,
-    TokenStreamSpecific<Unit, AnyCharsAccess>& tokenStream) {
+    TokenStreamSpecific<Unit, AnyCharsAccess>& tokenStream)
+    : currentToken(tokenStream.anyCharsAccess().currentToken()) {
   TokenStreamAnyChars& anyChars = tokenStream.anyCharsAccess();
 
   buf =
       tokenStream.sourceUnits.addressOfNextCodeUnit(/* allowPoisoned = */ true);
   flags = anyChars.flags;
   lineno = anyChars.lineno;
   linebase = anyChars.linebase;
   prevLinebase = anyChars.prevLinebase;
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1801,16 +1801,17 @@ bool GCRuntime::addBlackRootsTracer(JSTr
 }
 
 void GCRuntime::removeBlackRootsTracer(JSTraceDataOp traceOp, void* data) {
   // Can be called from finalizers
   for (size_t i = 0; i < blackRootTracers.ref().length(); i++) {
     Callback<JSTraceDataOp>* e = &blackRootTracers.ref()[i];
     if (e->op == traceOp && e->data == data) {
       blackRootTracers.ref().erase(e);
+      break;
     }
   }
 }
 
 void GCRuntime::setGrayRootsTracer(JSTraceDataOp traceOp, void* data) {
   AssertHeapIsIdle();
   grayRootTracer.op = traceOp;
   grayRootTracer.data = data;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -987,27 +987,26 @@ void LazyScript::traceChildren(JSTracer*
     TraceGenericPointerRoot(
         trc,
         reinterpret_cast<Cell**>(
             enclosingLazyScriptOrScope_.unsafeUnbarrieredForTracing()),
         "enclosingScope or enclosingLazyScript");
   }
 
   // We rely on the fact that atoms are always tenured.
-  JSAtom** closedOverBindings = this->closedOverBindings();
-  for (auto i : IntegerRange(numClosedOverBindings())) {
-    if (closedOverBindings[i]) {
-      TraceManuallyBarrieredEdge(trc, &closedOverBindings[i],
-                                 "closedOverBinding");
+  for (GCPtrAtom& closedOverBinding : closedOverBindings()) {
+    if (closedOverBinding) {
+      TraceEdge(trc, &closedOverBinding, "closedOverBinding");
     }
   }
 
-  GCPtrFunction* innerFunctions = this->innerFunctions();
-  for (auto i : IntegerRange(numInnerFunctions())) {
-    TraceEdge(trc, &innerFunctions[i], "lazyScriptInnerFunction");
+  for (GCPtrFunction& innerFunction : innerFunctions()) {
+    if (innerFunction) {
+      TraceEdge(trc, &innerFunction, "lazyScriptInnerFunction");
+    }
   }
 
   if (trc->isMarkingTracer()) {
     GCMarker::fromTracer(trc)->markImplicitEdges(this);
   }
 }
 inline void js::GCMarker::eagerlyMarkChildren(LazyScript* thing) {
   if (thing->script_) {
@@ -1026,26 +1025,26 @@ inline void js::GCMarker::eagerlyMarkChi
     TraceManuallyBarrieredGenericPointerEdge(
         this,
         reinterpret_cast<Cell**>(
             thing->enclosingLazyScriptOrScope_.unsafeUnbarrieredForTracing()),
         "enclosingScope or enclosingLazyScript");
   }
 
   // We rely on the fact that atoms are always tenured.
-  JSAtom** closedOverBindings = thing->closedOverBindings();
-  for (auto i : IntegerRange(thing->numClosedOverBindings())) {
-    if (closedOverBindings[i]) {
-      traverseEdge(thing, static_cast<JSString*>(closedOverBindings[i]));
+  for (GCPtrAtom& closedOverBinding : thing->closedOverBindings()) {
+    if (closedOverBinding) {
+      traverseEdge(thing, static_cast<JSString*>(closedOverBinding));
     }
   }
 
-  GCPtrFunction* innerFunctions = thing->innerFunctions();
-  for (auto i : IntegerRange(thing->numInnerFunctions())) {
-    traverseEdge(thing, static_cast<JSObject*>(innerFunctions[i]));
+  for (GCPtrFunction& innerFunction : thing->innerFunctions()) {
+    if (innerFunction) {
+      traverseEdge(thing, static_cast<JSObject*>(innerFunction));
+    }
   }
 
   markImplicitEdges(thing);
 }
 
 void Shape::traceChildren(JSTracer* trc) {
   TraceEdge(trc, &base_, "base");
   TraceEdge(trc, &propidRef(), "propid");
--- a/js/src/gc/PublicIterators.cpp
+++ b/js/src/gc/PublicIterators.cpp
@@ -81,21 +81,17 @@ void js::IterateChunks(JSContext* cx, vo
     chunkCallback(cx->runtime(), data, chunk);
   }
 }
 
 static void TraverseInnerLazyScriptsForLazyScript(
     JSContext* cx, void* data, LazyScript* enclosingLazyScript,
     IterateLazyScriptCallback lazyScriptCallback,
     const JS::AutoRequireNoGC& nogc) {
-  GCPtrFunction* innerFunctions = enclosingLazyScript->innerFunctions();
-  for (size_t i = 0, len = enclosingLazyScript->numInnerFunctions(); i < len;
-       i++) {
-    JSFunction* fun = innerFunctions[i];
-
+  for (JSFunction* fun : enclosingLazyScript->innerFunctions()) {
     // LazyScript::CreateForXDR temporarily initializes innerFunctions with
     // its own function, but it should be overwritten with correct
     // inner functions before getting inserted into parent's innerFunctions.
     MOZ_ASSERT(fun != enclosingLazyScript->functionNonDelazifying());
 
     if (!fun->isInterpretedLazy()) {
       return;
     }
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -33,27 +33,31 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Move.h"
 #include "mozilla/Range.h"
 
 #include "frontend/TokenStream.h"
 #include "gc/GC.h"
 #include "irregexp/RegExpCharacters.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 
 using namespace js;
 using namespace js::irregexp;
 
 using mozilla::AssertedCast;
 using mozilla::PointerRangeSize;
 
+using JS::RegExpFlag;
+using JS::RegExpFlags;
+
 // ----------------------------------------------------------------------------
 // RegExpBuilder
 
 RegExpBuilder::RegExpBuilder(LifoAlloc* alloc)
   : alloc(alloc),
     pending_empty_(false),
     characters_(nullptr)
 #ifdef DEBUG
@@ -235,31 +239,31 @@ RegExpBuilder::AddQuantifierToAtom(int m
     last_added_ = ADD_TERM;
 #endif
 }
 
 // ----------------------------------------------------------------------------
 // RegExpParser
 
 template <typename CharT>
-RegExpParser<CharT>::RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
-                                  const CharT* chars, const CharT* end, bool multiline_mode,
-                                  bool unicode, bool ignore_case)
+RegExpParser<CharT>::RegExpParser(frontend::TokenStreamAnyChars& ts,
+                                  LifoAlloc* alloc, RegExpFlags flags,
+                                  const CharT* chars, const CharT* end)
   : ts(ts),
     alloc(alloc),
     captures_(nullptr),
     start_(chars),
     next_pos_(start_),
     end_(end),
     current_(kEndMarker),
     capture_count_(0),
     has_more_(true),
-    multiline_(multiline_mode),
-    unicode_(unicode),
-    ignore_case_(ignore_case),
+    multiline_(flags.multiline()),
+    unicode_(flags.unicode()),
+    ignore_case_(flags.ignoreCase()),
     simple_(false),
     contains_anchor_(false),
     is_scanned_for_captures_(false)
 {
     Advance();
 }
 
 static size_t ComputeColumn(const Latin1Char* begin, const Latin1Char* end) {
@@ -1926,23 +1930,22 @@ RegExpParser<CharT>::ParseDisjunction()
 }
 
 template class irregexp::RegExpParser<Latin1Char>;
 template class irregexp::RegExpParser<char16_t>;
 
 template <typename CharT>
 static bool
 ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
-             const CharT* chars, size_t length,
-             bool multiline, bool match_only, bool unicode, bool ignore_case,
-             bool global, bool sticky, RegExpCompileData* data)
+             const CharT* chars, size_t length, bool match_only,
+             RegExpFlags flags, RegExpCompileData* data)
 {
     // We shouldn't strip pattern for exec, or test with global/sticky,
     // to reflect correct match position and lastIndex.
-    if (match_only && !global && !sticky) {
+    if (match_only && !flags.global() && !flags.sticky()) {
         // Try to strip a leading '.*' from the RegExp, but only if it is not
         // followed by a '?' (which will affect how the .* is parsed). This
         // pattern will affect the captures produced by the RegExp, but not
         // whether there is a match or not.
         if (length >= 3 && chars[0] == '.' && chars[1] == '*' && chars[2] != '?') {
             chars += 2;
             length -= 2;
         }
@@ -1953,46 +1956,47 @@ ParsePattern(frontend::TokenStreamAnyCha
         // are sure this will not affect how the RegExp is parsed.
         if (length >= 3 && !HasRegExpMetaChars(chars, length - 2) &&
             chars[length - 2] == '.' && chars[length - 1] == '*')
         {
             length -= 2;
         }
     }
 
-    RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case);
+    RegExpParser<CharT> parser(ts, &alloc, flags, chars, chars + length);
     data->tree = parser.ParsePattern();
     if (!data->tree)
         return false;
 
     data->simple = parser.simple();
     data->contains_anchor = parser.contains_anchor();
     data->capture_count = parser.captures_started();
     return true;
 }
 
 bool
-irregexp::ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
-                       bool multiline, bool match_only, bool unicode, bool ignore_case,
-                       bool global, bool sticky, RegExpCompileData* data)
+irregexp::ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
+                       JSAtom* str, bool match_only, RegExpFlags flags,
+                       RegExpCompileData* data)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
            ? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(),
-                            multiline, match_only, unicode, ignore_case, global, sticky, data)
+                            match_only, flags, data)
            : ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(),
-                            multiline, match_only, unicode, ignore_case, global, sticky, data);
+                            match_only, flags, data);
 }
 
 template <typename CharT>
 static bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                    const CharT* chars, size_t length, bool unicode)
 {
-    RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, false);
+    RegExpParser<CharT> parser(ts, &alloc, unicode ? RegExpFlag::Unicode : 0,
+                               chars, chars + length);
     return parser.ParsePattern() != nullptr;
 }
 
 bool
 irregexp::ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                              bool unicode)
 {
     JS::AutoCheckCannotGC nogc;
--- a/js/src/irregexp/RegExpParser.h
+++ b/js/src/irregexp/RegExpParser.h
@@ -31,29 +31,29 @@
 #ifndef V8_PARSER_H_
 #define V8_PARSER_H_
 
 #include "mozilla/Range.h"
 
 #include <stdarg.h>
 
 #include "irregexp/RegExpAST.h"
+#include "js/RegExpFlags.h"
 
 namespace js {
 
 namespace frontend {
     class TokenStreamAnyChars;
 }
 
 namespace irregexp {
 
 extern bool
 ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
-             bool multiline, bool match_only, bool unicode, bool ignore_case,
-             bool global, bool sticky, RegExpCompileData* data);
+             bool match_only, JS::RegExpFlags flags, RegExpCompileData* data);
 
 extern bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                    bool unicode);
 
 extern bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                    const mozilla::Range<const char16_t> chars, bool unicode);
@@ -180,18 +180,17 @@ class RegExpBuilder
 // Characters parsed by RegExpParser can be either char16_t or kEndMarker.
 typedef uint32_t widechar;
 
 template <typename CharT>
 class RegExpParser
 {
   public:
     RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
-                 const CharT* chars, const CharT* end, bool multiline_mode, bool unicode,
-                 bool ignore_case);
+                 JS::RegExpFlags flags, const CharT* chars, const CharT* end);
 
     RegExpTree* ParsePattern();
     RegExpTree* ParseDisjunction();
     RegExpTree* ParseCharacterClass();
 
     // Parses a {...,...} quantifier and stores the range in the given
     // out parameters.
     bool ParseIntervalQuantifier(int* min_out, int* max_out);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -39,16 +39,17 @@
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
 #include "jit/VMFunctions.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag
 #include "util/Unicode.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
@@ -1938,17 +1939,17 @@ static bool PrepareAndExecuteRegExp(
 
   // ES6 21.2.2.2 step 2.
   // See RegExp.cpp ExecuteRegExp for more detail.
   {
     Label done;
 
     masm.branchTest32(Assembler::Zero,
                       Address(temp1, RegExpShared::offsetOfFlags()),
-                      Imm32(UnicodeFlag), &done);
+                      Imm32(int32_t(JS::RegExpFlag::Unicode)), &done);
 
     // If input is latin1, there should not be surrogate pair.
     masm.branchLatin1String(input, &done);
 
     // Check if |lastIndex > 0 && lastIndex < input->length()|.
     // lastIndex should already have no sign here.
     masm.branchTest32(Assembler::Zero, lastIndex, lastIndex, &done);
     masm.loadStringLength(input, temp2);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -19,16 +19,17 @@
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmInstance.h"
@@ -38,16 +39,18 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::AssertedCast;
 using mozilla::Maybe;
 
 using JS::DoubleNaNValue;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using JS::TrackedOutcome;
 
 namespace js {
 namespace jit {
 
 // Returns true if |native| can be inlined cross-realm. Especially inlined
 // natives that can allocate objects or throw exceptions shouldn't be inlined
 // cross-realm without a careful analysis because we might use the wrong realm!
@@ -619,28 +622,28 @@ IonBuilder::InliningResult IonBuilder::i
     }
 
     MInstruction* byteOffset = addTypedArrayByteOffset(thisArg);
     current->push(byteOffset);
     return InliningStatus_Inlined;
   }
 
   // Try to optimize RegExp getters.
-  RegExpFlag mask = NoFlags;
+  RegExpFlags mask = RegExpFlag::NoFlags;
   if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
     const Class* clasp = thisTypes->getKnownClass(constraints());
     if (clasp != &RegExpObject::class_) {
       return InliningStatus_NotInlined;
     }
 
     MLoadFixedSlot* flags =
         MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
     current->add(flags);
     flags->setResultType(MIRType::Int32);
-    MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask));
+    MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask.value()));
     current->add(maskConst);
     MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
     maskedFlag->setInt32Specialization();
     current->add(maskedFlag);
 
     MDefinition* result = convertToBoolean(maskedFlag);
     current->push(result);
     return InliningStatus_Inlined;
--- a/js/src/jsapi-tests/testRegExp.cpp
+++ b/js/src/jsapi-tests/testRegExp.cpp
@@ -1,13 +1,14 @@
 /* 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 "js/RegExp.h"
+#include "js/RegExpFlags.h"
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testObjectIsRegExp) {
   JS::RootedValue val(cx);
 
   bool isRegExp;
 
   EVAL("new Object", &val);
@@ -25,26 +26,29 @@ BEGIN_TEST(testObjectIsRegExp) {
 END_TEST(testObjectIsRegExp)
 
 BEGIN_TEST(testGetRegExpFlags) {
   JS::RootedValue val(cx);
   JS::RootedObject obj(cx);
 
   EVAL("/foopy/", &val);
   obj = val.toObjectOrNull();
-  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj), 0u);
+  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj),
+              JS::RegExpFlags(JS::RegExpFlag::NoFlags));
 
   EVAL("/foopy/g", &val);
   obj = val.toObjectOrNull();
-  CHECK(JS::GetRegExpFlags(cx, obj) == JS::RegExpFlags::Global);
+  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj),
+              JS::RegExpFlags(JS::RegExpFlag::Global));
 
   EVAL("/foopy/gi", &val);
   obj = val.toObjectOrNull();
-  CHECK(JS::GetRegExpFlags(cx, obj) ==
-        (JS::RegExpFlags::Global | JS::RegExpFlags::IgnoreCase));
+  CHECK_EQUAL(
+      JS::GetRegExpFlags(cx, obj),
+      JS::RegExpFlags(JS::RegExpFlag::Global | JS::RegExpFlag::IgnoreCase));
 
   return true;
 }
 END_TEST(testGetRegExpFlags)
 
 BEGIN_TEST(testGetRegExpSource) {
   JS::RootedValue val(cx);
   JS::RootedObject obj(cx);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -15,16 +15,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
 #include "js/CharacterEncoding.h"
 #include "js/Equality.h"  // JS::SameValue
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
   js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
  public:
@@ -154,16 +155,36 @@ class JSAPITest {
   }
 
   JSAPITestString toSource(int v) { return toSource((long)v); }
 
   JSAPITestString toSource(bool v) {
     return JSAPITestString(v ? "true" : "false");
   }
 
+  JSAPITestString toSource(JS::RegExpFlags flags) {
+    JSAPITestString str;
+    if (flags.global()) {
+      str += "g";
+    }
+    if (flags.ignoreCase()) {
+      str += "i";
+    }
+    if (flags.multiline()) {
+      str += "m";
+    }
+    if (flags.unicode()) {
+      str += "u";
+    }
+    if (flags.sticky()) {
+      str += "y";
+    }
+    return str;
+  }
+
   JSAPITestString toSource(JSAtom* v) {
     JS::RootedValue val(cx, JS::StringValue((JSString*)v));
     return jsvalToSource(val);
   }
 
   // Note that in some still-supported GCC versions (we think anything before
   // GCC 4.6), this template does not work when the second argument is
   // nullptr. It infers type U = long int. Use CHECK_NULL instead.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -156,16 +156,17 @@ EXPORTS.js += [
     '../public/PropertyDescriptor.h',
     '../public/PropertySpec.h',
     '../public/ProtoKey.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RealmOptions.h',
     '../public/RefCounted.h',
     '../public/RegExp.h',
+    '../public/RegExpFlags.h',
     '../public/RequiredDefines.h',
     '../public/Result.h',
     '../public/RootingAPI.h',
     '../public/SavedFrameAPI.h',
     '../public/SharedArrayBuffer.h',
     '../public/SliceBudget.h',
     '../public/SourceText.h',
     '../public/StableStringChars.h',
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -231,33 +231,33 @@ template XDRResult js::XDRScriptConst(XD
                                       MutableHandleValue);
 
 // Code LazyScript's closed over bindings.
 template <XDRMode mode>
 static XDRResult XDRLazyClosedOverBindings(XDRState<mode>* xdr,
                                            MutableHandle<LazyScript*> lazy) {
   JSContext* cx = xdr->cx();
   RootedAtom atom(cx);
-  for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
+  for (GCPtrAtom& elem : lazy->closedOverBindings()) {
     uint8_t endOfScopeSentinel;
     if (mode == XDR_ENCODE) {
-      atom = lazy->closedOverBindings()[i];
+      atom = elem.get();
       endOfScopeSentinel = !atom;
     }
 
     MOZ_TRY(xdr->codeUint8(&endOfScopeSentinel));
 
     if (endOfScopeSentinel) {
       atom = nullptr;
     } else {
       MOZ_TRY(XDRAtom(xdr, &atom));
     }
 
     if (mode == XDR_DECODE) {
-      lazy->closedOverBindings()[i] = atom;
+      elem.init(atom);
     }
   }
 
   return Ok();
 }
 
 // Code the missing part needed to re-create a LazyScript from a JSScript.
 template <XDRMode mode>
@@ -266,28 +266,30 @@ static XDRResult XDRRelazificationInfo(X
                                        HandleScope enclosingScope,
                                        MutableHandle<LazyScript*> lazy) {
   MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiableIgnoringJitCode() &&
                                         script->maybeLazyScript());
   MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
 
   JSContext* cx = xdr->cx();
 
-  uint64_t packedFields;
+  uint32_t immutableFlags;
+  uint32_t numClosedOverBindings;
   {
     uint32_t sourceStart = script->sourceStart();
     uint32_t sourceEnd = script->sourceEnd();
     uint32_t toStringStart = script->toStringStart();
     uint32_t toStringEnd = script->toStringEnd();
     uint32_t lineno = script->lineno();
     uint32_t column = script->column();
     uint32_t numFieldInitializers;
 
     if (mode == XDR_ENCODE) {
-      packedFields = lazy->packedFieldsForXDR();
+      immutableFlags = lazy->immutableFlags();
+      numClosedOverBindings = lazy->numClosedOverBindings();
       MOZ_ASSERT(sourceStart == lazy->sourceStart());
       MOZ_ASSERT(sourceEnd == lazy->sourceEnd());
       MOZ_ASSERT(toStringStart == lazy->toStringStart());
       MOZ_ASSERT(toStringEnd == lazy->toStringEnd());
       MOZ_ASSERT(lineno == lazy->lineno());
       MOZ_ASSERT(column == lazy->column());
       // We can assert we have no inner functions because we don't
       // relazify scripts with inner functions.  See
@@ -296,29 +298,30 @@ static XDRResult XDRRelazificationInfo(X
       if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
         numFieldInitializers =
             (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
       } else {
         numFieldInitializers = UINT32_MAX;
       }
     }
 
-    MOZ_TRY(xdr->codeUint64(&packedFields));
+    MOZ_TRY(xdr->codeUint32(&immutableFlags));
     MOZ_TRY(xdr->codeUint32(&numFieldInitializers));
+    MOZ_TRY(xdr->codeUint32(&numClosedOverBindings));
 
     if (mode == XDR_DECODE) {
       RootedScriptSourceObject sourceObject(cx, script->sourceObject());
       lazy.set(LazyScript::CreateForXDR(
-          cx, fun, script, enclosingScope, sourceObject, packedFields,
-          sourceStart, sourceEnd, toStringStart, lineno, column));
+          cx, numClosedOverBindings, /* numInnerFunctions = */ 0, fun, script,
+          enclosingScope, sourceObject, immutableFlags, sourceStart, sourceEnd,
+          toStringStart, toStringEnd, lineno, column));
       if (!lazy) {
         return xdr->fail(JS::TranscodeResult_Throw);
       }
 
-      lazy->setToStringEnd(toStringEnd);
       if (numFieldInitializers != UINT32_MAX) {
         lazy->setFieldInitializers(
             FieldInitializers((size_t)numFieldInitializers));
       }
     }
   }
 
   // Code binding names.
@@ -1046,85 +1049,91 @@ XDRResult js::XDRLazyScript(XDRState<mod
 
   {
     uint32_t sourceStart;
     uint32_t sourceEnd;
     uint32_t toStringStart;
     uint32_t toStringEnd;
     uint32_t lineno;
     uint32_t column;
-    uint64_t packedFields;
+    uint32_t immutableFlags;
     uint32_t numFieldInitializers;
+    uint32_t numClosedOverBindings;
+    uint32_t numInnerFunctions;
 
     if (mode == XDR_ENCODE) {
       // Note: it's possible the LazyScript has a non-null script_ pointer
       // to a JSScript. We don't encode it: we can just delazify the
       // lazy script.
 
       MOZ_ASSERT(fun == lazy->functionNonDelazifying());
 
       sourceStart = lazy->sourceStart();
       sourceEnd = lazy->sourceEnd();
       toStringStart = lazy->toStringStart();
       toStringEnd = lazy->toStringEnd();
       lineno = lazy->lineno();
       column = lazy->column();
-      packedFields = lazy->packedFieldsForXDR();
+      immutableFlags = lazy->immutableFlags();
       if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
         numFieldInitializers =
             (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
       } else {
         numFieldInitializers = UINT32_MAX;
       }
+      numClosedOverBindings = lazy->numClosedOverBindings();
+      numInnerFunctions = lazy->numInnerFunctions();
     }
 
     MOZ_TRY(xdr->codeUint32(&sourceStart));
     MOZ_TRY(xdr->codeUint32(&sourceEnd));
     MOZ_TRY(xdr->codeUint32(&toStringStart));
     MOZ_TRY(xdr->codeUint32(&toStringEnd));
     MOZ_TRY(xdr->codeUint32(&lineno));
     MOZ_TRY(xdr->codeUint32(&column));
-    MOZ_TRY(xdr->codeUint64(&packedFields));
+    MOZ_TRY(xdr->codeUint32(&immutableFlags));
     MOZ_TRY(xdr->codeUint32(&numFieldInitializers));
+    MOZ_TRY(xdr->codeUint32(&numClosedOverBindings));
+    MOZ_TRY(xdr->codeUint32(&numInnerFunctions));
 
     if (mode == XDR_DECODE) {
       lazy.set(LazyScript::CreateForXDR(
-          cx, fun, nullptr, enclosingScope, sourceObject, packedFields,
-          sourceStart, sourceEnd, toStringStart, lineno, column));
+          cx, numClosedOverBindings, numInnerFunctions, fun, nullptr,
+          enclosingScope, sourceObject, immutableFlags, sourceStart, sourceEnd,
+          toStringStart, toStringEnd, lineno, column));
       if (!lazy) {
         return xdr->fail(JS::TranscodeResult_Throw);
       }
-      lazy->setToStringEnd(toStringEnd);
+
       if (numFieldInitializers != UINT32_MAX) {
         lazy->setFieldInitializers(
             FieldInitializers((size_t)numFieldInitializers));
       }
+
       fun->initLazyScript(lazy);
     }
   }
 
   // Code closed-over bindings.
   MOZ_TRY(XDRLazyClosedOverBindings(xdr, lazy));
 
   // Code inner functions.
   {
     RootedFunction func(cx);
-    GCPtrFunction* innerFunctions = lazy->innerFunctions();
-    size_t numInnerFunctions = lazy->numInnerFunctions();
-    for (size_t i = 0; i < numInnerFunctions; i++) {
+    for (GCPtrFunction& elem : lazy->innerFunctions()) {
       if (mode == XDR_ENCODE) {
-        func = innerFunctions[i];
+        func = elem.get();
       }
 
       MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, sourceObject, &func));
 
       if (mode == XDR_DECODE) {
-        innerFunctions[i] = func;
-        if (innerFunctions[i]->isInterpretedLazy()) {
-          innerFunctions[i]->lazyScript()->setEnclosingLazyScript(lazy);
+        elem.init(func);
+        if (elem->isInterpretedLazy()) {
+          elem->lazyScript()->setEnclosingLazyScript(lazy);
         }
       }
     }
   }
 
   return Ok();
 }
 
@@ -4580,17 +4589,17 @@ void JSScript::traceChildren(JSTracer* t
 
   jit::TraceJitScripts(trc, this);
 
   if (trc->isMarkingTracer()) {
     GCMarker::fromTracer(trc)->markImplicitEdges(this);
   }
 }
 
-void LazyScript::finalize(FreeOp* fop) { fop->free_(table_); }
+void LazyScript::finalize(FreeOp* fop) { fop->free_(lazyData_); }
 
 size_t JSScript::calculateLiveFixed(jsbytecode* pc) {
   size_t nlivefixed = numAlwaysLiveFixedSlots();
 
   if (nfixed() != nlivefixed) {
     Scope* scope = lookupScope(pc);
     if (scope) {
       scope = MaybeForwarded(scope);
@@ -4804,26 +4813,110 @@ bool JSScript::formalIsAliased(unsigned 
   }
   MOZ_CRASH("Argument slot not found");
 }
 
 bool JSScript::formalLivesInArgumentsObject(unsigned argSlot) {
   return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
+/* static */ size_t LazyScriptData::AllocationSize(
+    uint32_t numClosedOverBindings, uint32_t numInnerFunctions) {
+  size_t size = sizeof(LazyScriptData);
+
+  size += numClosedOverBindings * sizeof(GCPtrAtom);
+  size += numInnerFunctions * sizeof(GCPtrFunction);
+
+  return size;
+}
+
+// Placement-new elements of an array. This should optimize away for types with
+// trivial default initiation.
+template <typename T>
+void LazyScriptData::initElements(size_t offset, size_t length) {
+  void* raw = offsetToPointer<void>(offset);
+  DefaultInitializeElements<T>(raw, length);
+}
+
+LazyScriptData::LazyScriptData(uint32_t numClosedOverBindings,
+                               uint32_t numInnerFunctions)
+    : numClosedOverBindings_(numClosedOverBindings),
+      numInnerFunctions_(numInnerFunctions) {
+  // Variable-length data begins immediately after LazyScriptData itself.
+  size_t cursor = sizeof(*this);
+
+  // Default-initialize trailing arrays.
+
+  static_assert(alignof(LazyScriptData) >= alignof(GCPtrAtom),
+                "Incompatible alignment");
+  initElements<GCPtrAtom>(cursor, numClosedOverBindings);
+  cursor += numClosedOverBindings * sizeof(GCPtrAtom);
+
+  static_assert(alignof(GCPtrAtom) >= alignof(GCPtrFunction),
+                "Incompatible alignment");
+  initElements<GCPtrFunction>(cursor, numInnerFunctions);
+  cursor += numInnerFunctions * sizeof(GCPtrFunction);
+
+  // Sanity check
+  MOZ_ASSERT(AllocationSize(numClosedOverBindings, numInnerFunctions) ==
+             cursor);
+}
+
+/* static */ LazyScriptData* LazyScriptData::new_(
+    JSContext* cx, uint32_t numClosedOverBindings, uint32_t numInnerFunctions) {
+  // Compute size including trailing arrays
+  size_t size = AllocationSize(numClosedOverBindings, numInnerFunctions);
+
+  // Allocate contiguous raw buffer
+  void* raw = cx->pod_malloc<uint8_t>(size);
+  MOZ_ASSERT(uintptr_t(raw) % alignof(LazyScriptData) == 0);
+  if (!raw) {
+    return nullptr;
+  }
+
+  // Constuct the LazyScriptData. Trailing arrays are uninitialized but
+  // GCPtrs are put into a safe state.
+  return new (raw) LazyScriptData(numClosedOverBindings, numInnerFunctions);
+}
+
+mozilla::Span<GCPtrAtom> LazyScriptData::closedOverBindings() {
+  size_t offset = sizeof(LazyScriptData);
+  return mozilla::MakeSpan(offsetToPointer<GCPtrAtom>(offset),
+                           numClosedOverBindings_);
+}
+
+mozilla::Span<GCPtrFunction> LazyScriptData::innerFunctions() {
+  size_t offset =
+      sizeof(LazyScriptData) + sizeof(GCPtrAtom) * numClosedOverBindings_;
+  return mozilla::MakeSpan(offsetToPointer<GCPtrFunction>(offset),
+                           numInnerFunctions_);
+}
+
+void LazyScriptData::trace(JSTracer* trc) {
+  if (numClosedOverBindings_) {
+    auto array = closedOverBindings();
+    TraceRange(trc, array.size(), array.data(), "closedOverBindings");
+  }
+
+  if (numInnerFunctions_) {
+    auto array = innerFunctions();
+    TraceRange(trc, array.size(), array.data(), "innerFunctions");
+  }
+}
+
 LazyScript::LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
-                       void* table, uint64_t packedFields, uint32_t sourceStart,
-                       uint32_t sourceEnd, uint32_t toStringStart,
-                       uint32_t lineno, uint32_t column)
+                       LazyScriptData* data, uint32_t immutableFlags,
+                       uint32_t sourceStart, uint32_t sourceEnd,
+                       uint32_t toStringStart, uint32_t lineno, uint32_t column)
     : script_(nullptr),
       function_(fun),
       sourceObject_(&sourceObject),
-      table_(table),
-      packedFields_(packedFields),
-      fieldInitializers_(FieldInitializers::Invalid()),
+      lazyData_(data),
+      immutableFlags_(immutableFlags),
+      mutableFlags_(0),
       sourceStart_(sourceStart),
       sourceEnd_(sourceEnd),
       toStringStart_(toStringStart),
       toStringEnd_(sourceEnd),
       lineno_(lineno),
       column_(column) {
   MOZ_ASSERT(function_);
   MOZ_ASSERT(sourceObject_);
@@ -4869,158 +4962,103 @@ ScriptSourceObject& LazyScript::sourceOb
 
 ScriptSource* LazyScript::maybeForwardedScriptSource() const {
   JSObject* source = MaybeForwarded(&sourceObject());
   return UncheckedUnwrapWithoutExpose(source)
       ->as<ScriptSourceObject>()
       .source();
 }
 
-uint64_t LazyScript::packedFieldsForXDR() const {
-  union {
-    PackedView p;
-    uint64_t packedFields;
-  };
-
-  packedFields = packedFields_;
-
-  // Reset runtime flags
-  p.hasBeenCloned = false;
-
-  return packedFields;
-}
-
 /* static */
-LazyScript* LazyScript::CreateRaw(JSContext* cx, HandleFunction fun,
+LazyScript* LazyScript::CreateRaw(JSContext* cx, uint32_t numClosedOverBindings,
+                                  uint32_t numInnerFunctions,
+                                  HandleFunction fun,
                                   HandleScriptSourceObject sourceObject,
-                                  uint64_t packedFields, uint32_t sourceStart,
+                                  uint32_t immutableFlags, uint32_t sourceStart,
                                   uint32_t sourceEnd, uint32_t toStringStart,
                                   uint32_t lineno, uint32_t column) {
   cx->check(fun);
 
   MOZ_ASSERT(sourceObject);
-  union {
-    PackedView p;
-    uint64_t packed;
-  };
-
-  packed = packedFields;
-
-  // Reset runtime flags to obtain a fresh LazyScript.
-  p.hasBeenCloned = false;
-
-  size_t bytes = (p.numClosedOverBindings * sizeof(JSAtom*)) +
-                 (p.numInnerFunctions * sizeof(GCPtrFunction));
-
-  UniquePtr<uint8_t, JS::FreePolicy> table;
-  if (bytes) {
-    table.reset(cx->pod_malloc<uint8_t>(bytes));
-    if (!table) {
+
+  // Allocate a LazyScriptData if it will not be empty. Lazy class constructors
+  // also need LazyScriptData for field lists.
+  Rooted<UniquePtr<LazyScriptData>> data(cx);
+  if (numClosedOverBindings || numInnerFunctions || fun->isClassConstructor()) {
+    data.reset(
+        LazyScriptData::new_(cx, numClosedOverBindings, numInnerFunctions));
+    if (!data) {
       return nullptr;
     }
   }
 
   LazyScript* res = Allocate<LazyScript>(cx);
   if (!res) {
     return nullptr;
   }
 
   cx->realm()->scheduleDelazificationForDebugger();
 
   return new (res)
-      LazyScript(fun, *sourceObject, table.release(), packed, sourceStart,
-                 sourceEnd, toStringStart, lineno, column);
+      LazyScript(fun, *sourceObject, data.release(), immutableFlags,
+                 sourceStart, sourceEnd, toStringStart, lineno, column);
 }
 
 /* static */
 LazyScript* LazyScript::Create(JSContext* cx, HandleFunction fun,
                                HandleScriptSourceObject sourceObject,
                                const frontend::AtomVector& closedOverBindings,
                                Handle<GCVector<JSFunction*, 8>> innerFunctions,
                                uint32_t sourceStart, uint32_t sourceEnd,
                                uint32_t toStringStart, uint32_t lineno,
                                uint32_t column, frontend::ParseGoal parseGoal) {
-  union {
-    PackedView p;
-    uint64_t packedFields;
-  };
-
-  p.shouldDeclareArguments = false;
-  p.hasThisBinding = false;
-  p.isAsync = false;
-  p.hasRest = false;
-  p.numClosedOverBindings = closedOverBindings.length();
-  p.numInnerFunctions = innerFunctions.length();
-  p.isGenerator = false;
-  p.strict = false;
-  p.bindingsAccessedDynamically = false;
-  p.hasDebuggerStatement = false;
-  p.hasDirectEval = false;
-  p.isLikelyConstructorWrapper = false;
-  p.treatAsRunOnce = false;
-  p.isDerivedClassConstructor = false;
-  p.needsHomeObject = false;
-  p.isBinAST = false;
-  p.parseGoal = uint32_t(parseGoal);
-
-  LazyScript* res =
-      LazyScript::CreateRaw(cx, fun, sourceObject, packedFields, sourceStart,
-                            sourceEnd, toStringStart, lineno, column);
+  uint32_t immutableFlags = 0;
+  if (parseGoal == frontend::ParseGoal::Module) {
+    immutableFlags |= uint32_t(ImmutableFlags::IsModule);
+  }
+
+  LazyScript* res = LazyScript::CreateRaw(
+      cx, closedOverBindings.length(), innerFunctions.length(), fun,
+      sourceObject, immutableFlags, sourceStart, sourceEnd, toStringStart,
+      lineno, column);
   if (!res) {
     return nullptr;
   }
 
-  JSAtom** resClosedOverBindings = res->closedOverBindings();
+  mozilla::Span<GCPtrAtom> resClosedOverBindings = res->closedOverBindings();
   for (size_t i = 0; i < res->numClosedOverBindings(); i++) {
-    resClosedOverBindings[i] = closedOverBindings[i];
-  }
-
-  GCPtrFunction* resInnerFunctions = res->innerFunctions();
+    resClosedOverBindings[i].init(closedOverBindings[i]);
+  }
+
+  mozilla::Span<GCPtrFunction> resInnerFunctions = res->innerFunctions();
   for (size_t i = 0; i < res->numInnerFunctions(); i++) {
     resInnerFunctions[i].init(innerFunctions[i]);
     if (resInnerFunctions[i]->isInterpretedLazy()) {
       resInnerFunctions[i]->lazyScript()->setEnclosingLazyScript(res);
     }
   }
 
   return res;
 }
 
 /* static */
 LazyScript* LazyScript::CreateForXDR(
-    JSContext* cx, HandleFunction fun, HandleScript script,
-    HandleScope enclosingScope, HandleScriptSourceObject sourceObject,
-    uint64_t packedFields, uint32_t sourceStart, uint32_t sourceEnd,
-    uint32_t toStringStart, uint32_t lineno, uint32_t column) {
-  // Dummy atom which is not a valid property name.
-  RootedAtom dummyAtom(cx, cx->names().comma);
-
-  // Dummy function which is not a valid function as this is the one which is
-  // holding this lazy script.
-  HandleFunction dummyFun = fun;
-
-  LazyScript* res =
-      LazyScript::CreateRaw(cx, fun, sourceObject, packedFields, sourceStart,
-                            sourceEnd, toStringStart, lineno, column);
+    JSContext* cx, uint32_t numClosedOverBindings, uint32_t numInnerFunctions,
+    HandleFunction fun, HandleScript script, HandleScope enclosingScope,
+    HandleScriptSourceObject sourceObject, uint32_t immutableFlags,
+    uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
+    uint32_t toStringEnd, uint32_t lineno, uint32_t column) {
+  LazyScript* res = LazyScript::CreateRaw(
+      cx, numClosedOverBindings, numInnerFunctions, fun, sourceObject,
+      immutableFlags, sourceStart, sourceEnd, toStringStart, lineno, column);
   if (!res) {
     return nullptr;
   }
 
-  // Fill with dummies, to be GC-safe after the initialization of the free
-  // variables and inner functions.
-  size_t i, num;
-  JSAtom** closedOverBindings = res->closedOverBindings();
-  for (i = 0, num = res->numClosedOverBindings(); i < num; i++) {
-    closedOverBindings[i] = dummyAtom;
-  }
-
-  GCPtrFunction* functions = res->innerFunctions();
-  for (i = 0, num = res->numInnerFunctions(); i < num; i++) {
-    functions[i].init(dummyFun);
-  }
+  res->setToStringEnd(toStringEnd);
 
   // Set the enclosing scope of the lazy function. This value should only be
   // set if we have a non-lazy enclosing script at this point.
   // LazyScript::enclosingScriptHasEverBeenCompiled relies on the enclosing
   // scope being non-null if we have ever been nested inside non-lazy
   // function.
   MOZ_ASSERT(!res->hasEnclosingScope());
   if (enclosingScope) {
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1764,16 +1764,22 @@ class JSScript : public js::gc::TenuredC
     // Script came from eval().
     IsForEval = 1 << 22,
 
     // Whether this is a top-level module script.
     IsModule = 1 << 23,
 
     // Whether this function needs a call object or named lambda environment.
     NeedsFunctionEnvironmentObjects = 1 << 24,
+
+    // LazyScript flags
+    ShouldDeclareArguments = 1 << 25,
+    IsBinAST = 1 << 26,
+    HasDebuggerStatement = 1 << 27,
+    HasDirectEval = 1 << 28,
   };
 
  private:
   // Note: don't make this a bitfield! It makes it hard to read these flags
   // from JIT code.
   uint32_t immutableFlags_ = 0;
 
   // Mutable flags typically store information about runtime or deoptimization
@@ -2873,16 +2879,57 @@ struct FieldInitializers {
       :
 #ifdef DEBUG
         valid(false),
 #endif
         numFieldInitializers(0) {
   }
 };
 
+// Variable-length data for LazyScripts. Contains vector of inner functions and
+// vector of captured property ids.
+class alignas(uintptr_t) LazyScriptData final {
+ private:
+  uint32_t numClosedOverBindings_ = 0;
+  uint32_t numInnerFunctions_ = 0;
+
+  FieldInitializers fieldInitializers_ = FieldInitializers::Invalid();
+
+  // Size to allocate
+  static size_t AllocationSize(uint32_t numClosedOverBindings,
+                               uint32_t numInnerFunctions);
+
+  // Translate an offset into a concrete pointer.
+  template <typename T>
+  T* offsetToPointer(size_t offset) {
+    uintptr_t base = reinterpret_cast<uintptr_t>(this);
+    return reinterpret_cast<T*>(base + offset);
+  }
+
+  template <typename T>
+  void initElements(size_t offset, size_t length);
+
+  LazyScriptData(uint32_t numClosedOverBindings, uint32_t numInnerFunctions);
+
+ public:
+  static LazyScriptData* new_(JSContext* cx, uint32_t numClosedOverBindings,
+                              uint32_t numInnerFunctions);
+
+  friend class LazyScript;
+
+  mozilla::Span<GCPtrAtom> closedOverBindings();
+  mozilla::Span<GCPtrFunction> innerFunctions();
+
+  void trace(JSTracer* trc);
+
+  // LazyScriptData has trailing data so isn't copyable or movable.
+  LazyScriptData(const LazyScriptData&) = delete;
+  LazyScriptData& operator=(const LazyScriptData&) = delete;
+};
+
 // Information about a script which may be (or has been) lazily compiled to
 // bytecode from its source.
 class LazyScript : public gc::TenuredCell {
   // If non-nullptr, the script has been compiled and this is a forwarding
   // pointer to the result. This is a weak pointer: after relazification, we
   // can collect the script if there are no other pointers to it.
   WeakRef<JSScript*> script_;
 
@@ -2965,115 +3012,97 @@ class LazyScript : public gc::TenuredCel
   // | enclosing Scope |<-------------+
   // +-----------------+
   GCPtr<TenuredCell*> enclosingLazyScriptOrScope_;
 
   // ScriptSourceObject. We leave this set to nullptr until we generate
   // bytecode for our immediate parent.
   GCPtr<ScriptSourceObject*> sourceObject_;
 
-  // Heap allocated table with any free variables or inner functions.
-  void* table_;
+  // Heap allocated table with any free variables, inner functions, or class
+  // fields. This will be nullptr if none exist.
+  LazyScriptData* lazyData_;
 
   static const uint32_t NumClosedOverBindingsBits = 20;
   static const uint32_t NumInnerFunctionsBits = 20;
 
-  struct PackedView {
-    uint32_t shouldDeclareArguments : 1;
-    uint32_t hasThisBinding : 1;
-    uint32_t isAsync : 1;
-    uint32_t isBinAST : 1;
-
-    uint32_t numClosedOverBindings : NumClosedOverBindingsBits;
-
-    // -- 32bit boundary --
-
-    uint32_t numInnerFunctions : NumInnerFunctionsBits;
-
-    // N.B. These are booleans but need to be uint32_t to pack correctly on
-    // MSVC. If you add another boolean here, make sure to initialize it in
-    // LazyScript::Create().
-    uint32_t isGenerator : 1;
-    uint32_t strict : 1;
-    uint32_t bindingsAccessedDynamically : 1;
-    uint32_t hasDebuggerStatement : 1;
-    uint32_t hasDirectEval : 1;
-    uint32_t isLikelyConstructorWrapper : 1;
-    uint32_t treatAsRunOnce : 1;
-    uint32_t isDerivedClassConstructor : 1;
-    uint32_t needsHomeObject : 1;
-    uint32_t hasRest : 1;
-    uint32_t parseGoal : 1;
-
-    // Runtime flags
-    uint32_t hasBeenCloned : 1;
-  };
-
-  union {
-    PackedView p_;
-    uint64_t packedFields_;
-  };
-
-  FieldInitializers fieldInitializers_;
+  // See: JSScript::ImmutableFlags / MutableFlags.
+  // NOTE: Lazy script only defines and uses a subset of these flags.
+  using ImmutableFlags = JSScript::ImmutableFlags;
+  using MutableFlags = JSScript::MutableFlags;
+
+  uint32_t immutableFlags_;
+  uint32_t mutableFlags_;
+
+  MOZ_MUST_USE bool hasFlag(MutableFlags flag) const {
+    return mutableFlags_ & uint32_t(flag);
+  }
+  void setFlag(MutableFlags flag) { mutableFlags_ |= uint32_t(flag); }
+
+  MOZ_MUST_USE bool hasFlag(ImmutableFlags flag) const {
+    return immutableFlags_ & uint32_t(flag);
+  }
+  void setFlag(ImmutableFlags flag) { immutableFlags_ |= uint32_t(flag); }
 
   // Source location for the script.
   // See the comment in JSScript for the details
   uint32_t sourceStart_;
   uint32_t sourceEnd_;
   uint32_t toStringStart_;
   uint32_t toStringEnd_;
   // Line and column of |begin_| position, that is the position where we
   // start parsing.
   uint32_t lineno_;
   uint32_t column_;
 
-  LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject, void* table,
-             uint64_t packedFields, uint32_t begin, uint32_t end,
-             uint32_t toStringStart, uint32_t lineno, uint32_t column);
+  LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
+             LazyScriptData* data, uint32_t immutableFlags,
+             uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
+             uint32_t lineno, uint32_t column);
 
   // Create a LazyScript without initializing the closedOverBindings and the
   // innerFunctions. To be GC-safe, the caller must initialize both vectors
   // with valid atoms and functions.
-  static LazyScript* CreateRaw(JSContext* cx, HandleFunction fun,
+  static LazyScript* CreateRaw(JSContext* cx, uint32_t numClosedOverBindings,
+                               uint32_t numInnerFunctions, HandleFunction fun,
                                HandleScriptSourceObject sourceObject,
-                               uint64_t packedData, uint32_t begin,
-                               uint32_t end, uint32_t toStringStart,
+                               uint32_t immutableFlags, uint32_t sourceStart,
+                               uint32_t sourceEnd, uint32_t toStringStart,
                                uint32_t lineno, uint32_t column);
 
  public:
   static const uint32_t NumClosedOverBindingsLimit =
       1 << NumClosedOverBindingsBits;
   static const uint32_t NumInnerFunctionsLimit = 1 << NumInnerFunctionsBits;
 
   // Create a LazyScript and initialize closedOverBindings and innerFunctions
   // with the provided vectors.
   static LazyScript* Create(JSContext* cx, HandleFunction fun,
                             HandleScriptSourceObject sourceObject,
                             const frontend::AtomVector& closedOverBindings,
                             Handle<GCVector<JSFunction*, 8>> innerFunctions,
-                            uint32_t begin, uint32_t end,
+                            uint32_t sourceStart, uint32_t sourceEnd,
                             uint32_t toStringStart, uint32_t lineno,
                             uint32_t column, frontend::ParseGoal parseGoal);
 
   // Create a LazyScript and initialize the closedOverBindings and the
   // innerFunctions with dummy values to be replaced in a later initialization
   // phase.
   //
   // The "script" argument to this function can be null.  If it's non-null,
   // then this LazyScript should be associated with the given JSScript.
   //
   // The sourceObject and enclosingScope arguments may be null if the
   // enclosing function is also lazy.
-  static LazyScript* CreateForXDR(JSContext* cx, HandleFunction fun,
-                                  HandleScript script,
-                                  HandleScope enclosingScope,
-                                  HandleScriptSourceObject sourceObject,
-                                  uint64_t packedData, uint32_t begin,
-                                  uint32_t end, uint32_t toStringStart,
-                                  uint32_t lineno, uint32_t column);
+  static LazyScript* CreateForXDR(
+      JSContext* cx, uint32_t numClosedOverBindings, uint32_t numInnerFunctions,
+      HandleFunction fun, HandleScript script, HandleScope enclosingScope,
+      HandleScriptSourceObject sourceObject, uint32_t immutableFlags,
+      uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
+      uint32_t toStringEnd, uint32_t lineno, uint32_t column);
 
   static inline JSFunction* functionDelazifying(JSContext* cx,
                                                 Handle<LazyScript*>);
   JSFunction* functionNonDelazifying() const { return function_; }
 
   JS::Compartment* compartment() const;
   JS::Compartment* maybeCompartment() const { return compartment(); }
   Realm* realm() const;
@@ -3111,106 +3140,143 @@ class LazyScript : public gc::TenuredCel
     return enclosingScope()->hasOnChain(ScopeKind::NonSyntactic);
   }
 
   ScriptSourceObject& sourceObject() const;
   ScriptSource* scriptSource() const { return sourceObject().source(); }
   ScriptSource* maybeForwardedScriptSource() const;
   bool mutedErrors() const { return scriptSource()->mutedErrors(); }
 
-  uint32_t numClosedOverBindings() const { return p_.numClosedOverBindings; }
-  JSAtom** closedOverBindings() { return (JSAtom**)table_; }
-
-  uint32_t numInnerFunctions() const { return p_.numInnerFunctions; }
-  GCPtrFunction* innerFunctions() {
-    return (GCPtrFunction*)&closedOverBindings()[numClosedOverBindings()];
+  mozilla::Span<GCPtrAtom> closedOverBindings() {
+    return lazyData_ ? lazyData_->closedOverBindings()
+                     : mozilla::Span<GCPtrAtom>();
+  }
+  uint32_t numClosedOverBindings() const {
+    return lazyData_ ? lazyData_->closedOverBindings().size() : 0;
+  };
+
+  mozilla::Span<GCPtrFunction> innerFunctions() {
+    return lazyData_ ? lazyData_->innerFunctions()
+                     : mozilla::Span<GCPtrFunction>();
+  }
+  uint32_t numInnerFunctions() const {
+    return lazyData_ ? lazyData_->innerFunctions().size() : 0;
   }
 
   GeneratorKind generatorKind() const {
-    return p_.isGenerator ? GeneratorKind::Generator
-                          : GeneratorKind::NotGenerator;
+    return hasFlag(ImmutableFlags::IsGenerator) ? GeneratorKind::Generator
+                                                : GeneratorKind::NotGenerator;
   }
 
   bool isGenerator() const {
     return generatorKind() == GeneratorKind::Generator;
   }
 
   void setGeneratorKind(GeneratorKind kind) {
     // A script only gets its generator kind set as part of initialization,
     // so it can only transition from NotGenerator.
     MOZ_ASSERT(!isGenerator());
-    p_.isGenerator = kind == GeneratorKind::Generator;
+    if (kind == GeneratorKind::Generator) {
+      setFlag(ImmutableFlags::IsGenerator);
+    }
   }
 
+  bool isAsync() const { return hasFlag(ImmutableFlags::IsAsync); }
   FunctionAsyncKind asyncKind() const {
-    return p_.isAsync ? FunctionAsyncKind::AsyncFunction
-                      : FunctionAsyncKind::SyncFunction;
+    return isAsync() ? FunctionAsyncKind::AsyncFunction
+                     : FunctionAsyncKind::SyncFunction;
   }
-  bool isAsync() const { return p_.isAsync; }
 
   void setAsyncKind(FunctionAsyncKind kind) {
-    p_.isAsync = kind == FunctionAsyncKind::AsyncFunction;
+    if (kind == FunctionAsyncKind::AsyncFunction) {
+      setFlag(ImmutableFlags::IsAsync);
+    }
   }
 
-  bool hasRest() const { return p_.hasRest; }
-  void setHasRest() { p_.hasRest = true; }
+  bool hasRest() const { return hasFlag(ImmutableFlags::HasRest); }
+  void setHasRest() { setFlag(ImmutableFlags::HasRest); }
 
   frontend::ParseGoal parseGoal() const {
-    return frontend::ParseGoal(p_.parseGoal);
+    if (hasFlag(ImmutableFlags::IsModule)) {
+      return frontend::ParseGoal::Module;
+    }
+    return frontend::ParseGoal::Script;
   }
 
-  bool isBinAST() const { return p_.isBinAST; }
-  void setIsBinAST() { p_.isBinAST = true; }
-
-  bool strict() const { return p_.strict; }
-  void setStrict() { p_.strict = true; }
+  bool isBinAST() const { return hasFlag(ImmutableFlags::IsBinAST); }
+  void setIsBinAST() { setFlag(ImmutableFlags::IsBinAST); }
+
+  bool strict() const { return hasFlag(ImmutableFlags::Strict); }
+  void setStrict() { setFlag(ImmutableFlags::Strict); }
 
   bool bindingsAccessedDynamically() const {
-    return p_.bindingsAccessedDynamically;
+    return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
   }
   void setBindingsAccessedDynamically() {
-    p_.bindingsAccessedDynamically = true;
+    setFlag(ImmutableFlags::BindingsAccessedDynamically);
+  }
+
+  bool hasDebuggerStatement() const {
+    return hasFlag(ImmutableFlags::HasDebuggerStatement);
   }
-
-  bool hasDebuggerStatement() const { return p_.hasDebuggerStatement; }
-  void setHasDebuggerStatement() { p_.hasDebuggerStatement = true; }
-
-  bool hasDirectEval() const { return p_.hasDirectEval; }
-  void setHasDirectEval() { p_.hasDirectEval = true; }
+  void setHasDebuggerStatement() {
+    setFlag(ImmutableFlags::HasDebuggerStatement);
+  }
+
+  bool hasDirectEval() const { return hasFlag(ImmutableFlags::HasDirectEval); }
+  void setHasDirectEval() { setFlag(ImmutableFlags::HasDirectEval); }
 
   bool isLikelyConstructorWrapper() const {
-    return p_.isLikelyConstructorWrapper;
+    return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
+  }
+  void setLikelyConstructorWrapper() {
+    setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
   }
-  void setLikelyConstructorWrapper() { p_.isLikelyConstructorWrapper = true; }
-
-  bool hasBeenCloned() const { return p_.hasBeenCloned; }
-  void setHasBeenCloned() { p_.hasBeenCloned = true; }
-
-  bool treatAsRunOnce() const { return p_.treatAsRunOnce; }
-  void setTreatAsRunOnce() { p_.treatAsRunOnce = true; }
+
+  bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
+  void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
+
+  bool treatAsRunOnce() const {
+    return hasFlag(ImmutableFlags::TreatAsRunOnce);
+  }
+  void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
 
   bool isDerivedClassConstructor() const {
-    return p_.isDerivedClassConstructor;
+    return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
+  }
+  void setIsDerivedClassConstructor() {
+    setFlag(ImmutableFlags::IsDerivedClassConstructor);
+  }
+
+  bool needsHomeObject() const {
+    return hasFlag(ImmutableFlags::NeedsHomeObject);
   }
-  void setIsDerivedClassConstructor() { p_.isDerivedClassConstructor = true; }
-
-  bool needsHomeObject() const { return p_.needsHomeObject; }
-  void setNeedsHomeObject() { p_.needsHomeObject = true; }
-
-  bool shouldDeclareArguments() const { return p_.shouldDeclareArguments; }
-  void setShouldDeclareArguments() { p_.shouldDeclareArguments = true; }
-
-  bool hasThisBinding() const { return p_.hasThisBinding; }
-  void setHasThisBinding() { p_.hasThisBinding = true; }
+  void setNeedsHomeObject() { setFlag(ImmutableFlags::NeedsHomeObject); }
+
+  bool shouldDeclareArguments() const {
+    return hasFlag(ImmutableFlags::ShouldDeclareArguments);
+  }
+  void setShouldDeclareArguments() {
+    setFlag(ImmutableFlags::ShouldDeclareArguments);
+  }
+
+  bool hasThisBinding() const {
+    return hasFlag(ImmutableFlags::FunctionHasThisBinding);
+  }
+  void setHasThisBinding() { setFlag(ImmutableFlags::FunctionHasThisBinding); }
 
   void setFieldInitializers(FieldInitializers fieldInitializers) {
-    fieldInitializers_ = fieldInitializers;
+    MOZ_ASSERT(lazyData_);
+    lazyData_->fieldInitializers_ = fieldInitializers;
   }
 
-  FieldInitializers getFieldInitializers() const { return fieldInitializers_; }
+  FieldInitializers getFieldInitializers() const {
+    return lazyData_ ? lazyData_->fieldInitializers_
+                     : FieldInitializers::Invalid();
+  }
 
   const char* filename() const { return scriptSource()->filename(); }
   uint32_t sourceStart() const { return sourceStart_; }
   uint32_t sourceEnd() const { return sourceEnd_; }
   uint32_t sourceLength() const { return sourceEnd_ - sourceStart_; }
   uint32_t toStringStart() const { return toStringStart_; }
   uint32_t toStringEnd() const { return toStringEnd_; }
   uint32_t lineno() const { return lineno_; }
@@ -3234,20 +3300,20 @@ class LazyScript : public gc::TenuredCel
 
   friend class GCMarker;
   void traceChildren(JSTracer* trc);
   void finalize(js::FreeOp* fop);
 
   static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;
 
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
-    return mallocSizeOf(table_);
+    return mallocSizeOf(lazyData_);
   }
 
-  uint64_t packedFieldsForXDR() const;
+  uint32_t immutableFlags() const { return immutableFlags_; }
 };
 
 /* If this fails, add/remove padding within LazyScript. */
 static_assert(sizeof(LazyScript) % js::gc::CellAlignBytes == 0,
               "Size of LazyScript must be an integral multiple of "
               "js::gc::CellAlignBytes");
 
 struct ScriptAndCounts {
--- a/js/src/vm/RegExpConstants.h
+++ b/js/src/vm/RegExpConstants.h
@@ -6,34 +6,16 @@
 
 #ifndef vm_RegExpConstants_h
 #define vm_RegExpConstants_h
 
 #include "builtin/SelfHostingDefines.h"
 
 namespace js {
 
-enum RegExpFlag : uint8_t {
-  IgnoreCaseFlag = 0x01,
-  GlobalFlag = 0x02,
-  MultilineFlag = 0x04,
-  StickyFlag = 0x08,
-  UnicodeFlag = 0x10,
-
-  NoFlags = 0x00,
-  AllFlags = 0x1f
-};
-
-static_assert(IgnoreCaseFlag == REGEXP_IGNORECASE_FLAG &&
-                  GlobalFlag == REGEXP_GLOBAL_FLAG &&
-                  MultilineFlag == REGEXP_MULTILINE_FLAG &&
-                  StickyFlag == REGEXP_STICKY_FLAG &&
-                  UnicodeFlag == REGEXP_UNICODE_FLAG,
-              "Flag values should be in sync with self-hosted JS");
-
 enum RegExpRunStatus {
   RegExpRunStatus_Error,
   RegExpRunStatus_Success,
   RegExpRunStatus_Success_NotFound
 };
 
 } /* namespace js */
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -10,24 +10,26 @@
 #include "mozilla/PodOperations.h"
 
 #include "builtin/String.h"
 #ifdef DEBUG
 #  include "jsutil.h"
 #endif
 
 #include "builtin/RegExp.h"
+#include "builtin/SelfHostingDefines.h"  // REGEXP_*_FLAG
 #include "frontend/TokenStream.h"
 #include "gc/HashUtil.h"
 #ifdef DEBUG
 #  include "irregexp/RegExpBytecode.h"
 #endif
 #include "irregexp/RegExpParser.h"
 #include "jit/VMFunctions.h"
 #include "js/RegExp.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/StableStringChars.h"
 #include "util/StringBuffer.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #ifdef DEBUG
 #  include "util/Unicode.h"
@@ -37,33 +39,35 @@
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::AutoStableStringChars;
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using js::frontend::TokenStream;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 
 using JS::AutoCheckCannotGC;
 
-static_assert(IgnoreCaseFlag == JS::RegExpFlags::IgnoreCase,
-              "public/internal /i flag bits must agree");
-static_assert(GlobalFlag == JS::RegExpFlags::Global,
-              "public/internal /g flag bits must agree");
-static_assert(MultilineFlag == JS::RegExpFlags::Multiline,
-              "public/internal /m flag bits must agree");
-static_assert(StickyFlag == JS::RegExpFlags::Sticky,
-              "public/internal /y flag bits must agree");
-static_assert(UnicodeFlag == JS::RegExpFlags::Unicode,
-              "public/internal /u flag bits must agree");
+static_assert(RegExpFlag::Global == REGEXP_GLOBAL_FLAG,
+              "self-hosted JS and /g flag bits must agree");
+static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
+              "self-hosted JS and /i flag bits must agree");
+static_assert(RegExpFlag::Multiline == REGEXP_MULTILINE_FLAG,
+              "self-hosted JS and /m flag bits must agree");
+static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
+              "self-hosted JS and /u flag bits must agree");
+static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
+              "self-hosted JS and /y flag bits must agree");
 
 RegExpObject* js::RegExpAlloc(JSContext* cx, NewObjectKind newKind,
                               HandleObject proto /* = nullptr */) {
   Rooted<RegExpObject*> regexp(
       cx, NewObjectWithClassProto<RegExpObject>(cx, proto, newKind));
   if (!regexp) {
     return nullptr;
   }
@@ -112,35 +116,35 @@ RegExpShared* RegExpObject::getShared(JS
   if (regexp->hasShared()) {
     return regexp->sharedRef();
   }
 
   return createShared(cx, regexp);
 }
 
 /* static */
-bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask) {
+bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) {
   if (native == regexp_global) {
-    *mask = GlobalFlag;
+    *mask = RegExpFlag::Global;
     return true;
   }
   if (native == regexp_ignoreCase) {
-    *mask = IgnoreCaseFlag;
+    *mask = RegExpFlag::IgnoreCase;
     return true;
   }
   if (native == regexp_multiline) {
-    *mask = MultilineFlag;
+    *mask = RegExpFlag::Multiline;
     return true;
   }
   if (native == regexp_sticky) {
-    *mask = StickyFlag;
+    *mask = RegExpFlag::Sticky;
     return true;
   }
   if (native == regexp_unicode) {
-    *mask = UnicodeFlag;
+    *mask = RegExpFlag::Unicode;
     return true;
   }
 
   return false;
 }
 
 /* static */
 void RegExpObject::trace(JSTracer* trc, JSObject* obj) {
@@ -193,83 +197,83 @@ const Class RegExpObject::class_ = {
     &RegExpObjectClassOps, &RegExpObjectClassSpec};
 
 const Class RegExpObject::protoClass_ = {
     js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), JS_NULL_CLASS_OPS,
     &RegExpObjectClassSpec};
 
 template <typename CharT>
 RegExpObject* RegExpObject::create(JSContext* cx, const CharT* chars,
-                                   size_t length, RegExpFlag flags,
+                                   size_t length, RegExpFlags flags,
                                    frontend::TokenStreamAnyChars& tokenStream,
                                    NewObjectKind newKind) {
   static_assert(mozilla::IsSame<CharT, char16_t>::value,
                 "this code may need updating if/when CharT encodes UTF-8");
 
   RootedAtom source(cx, AtomizeChars(cx, chars, length));
   if (!source) {
     return nullptr;
   }
 
   return create(cx, source, flags, tokenStream, newKind);
 }
 
 template RegExpObject* RegExpObject::create(
-    JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
+    JSContext* cx, const char16_t* chars, size_t length, RegExpFlags flags,
     frontend::TokenStreamAnyChars& tokenStream, NewObjectKind newKind);
 
 template <typename CharT>
 RegExpObject* RegExpObject::create(JSContext* cx, const CharT* chars,
-                                   size_t length, RegExpFlag flags,
+                                   size_t length, RegExpFlags flags,
                                    NewObjectKind newKind) {
   static_assert(mozilla::IsSame<CharT, char16_t>::value,
                 "this code may need updating if/when CharT encodes UTF-8");
 
   RootedAtom source(cx, AtomizeChars(cx, chars, length));
   if (!source) {
     return nullptr;
   }
 
   return create(cx, source, flags, newKind);
 }
 
 template RegExpObject* RegExpObject::create(JSContext* cx,
                                             const char16_t* chars,
-                                            size_t length, RegExpFlag flags,
+                                            size_t length, RegExpFlags flags,
                                             NewObjectKind newKind);
 
 RegExpObject* RegExpObject::create(JSContext* cx, HandleAtom source,
-                                   RegExpFlag flags,
+                                   RegExpFlags flags,
                                    frontend::TokenStreamAnyChars& tokenStream,
                                    NewObjectKind newKind) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   if (!irregexp::ParsePatternSyntax(tokenStream, allocScope.alloc(), source,
-                                    flags & UnicodeFlag)) {
+                                    flags.unicode())) {
     return nullptr;
   }
 
   Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
   if (!regexp) {
     return nullptr;
   }
 
   regexp->initAndZeroLastIndex(source, flags, cx);
 
   return regexp;
 }
 
 RegExpObject* RegExpObject::create(JSContext* cx, HandleAtom source,
-                                   RegExpFlag flags, NewObjectKind newKind) {
+                                   RegExpFlags flags, NewObjectKind newKind) {
   CompileOptions dummyOptions(cx);
   TokenStream dummyTokenStream(cx, dummyOptions, (const char16_t*)nullptr, 0,
                                nullptr);
 
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   if (!irregexp::ParsePatternSyntax(dummyTokenStream, allocScope.alloc(),
-                                    source, flags & UnicodeFlag)) {
+                                    source, flags.unicode())) {
     return nullptr;
   }
 
   Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
   if (!regexp) {
     return nullptr;
   }
 
@@ -299,26 +303,26 @@ Shape* RegExpObject::assignInitialShape(
 
   JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
 
   /* The lastIndex property alone is writable but non-configurable. */
   return NativeObject::addDataProperty(cx, self, cx->names().lastIndex,
                                        LAST_INDEX_SLOT, JSPROP_PERMANENT);
 }
 
-void RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlag flags) {
+void RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlags flags) {
   // If this is a re-initialization with an existing RegExpShared, 'flags'
   // may not match getShared()->flags, so forget the RegExpShared.
   sharedRef() = nullptr;
 
   setSource(source);
   setFlags(flags);
 }
 
-void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlag flags,
+void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlags flags,
                                         JSContext* cx) {
   initIgnoringLastIndex(source, flags);
   zeroLastIndex(cx);
 }
 
 static MOZ_ALWAYS_INLINE bool IsRegExpLineTerminator(const JS::Latin1Char c) {
   return c == '\n' || c == '\r';
 }
@@ -893,17 +897,17 @@ bool js::StringHasRegExpMetaChars(JSLine
     return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
   }
 
   return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
 }
 
 /* RegExpShared */
 
-RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
+RegExpShared::RegExpShared(JSAtom* source, RegExpFlags flags)
     : source(source), flags(flags), canStringMatch(false), parenCount(0) {}
 
 void RegExpShared::traceChildren(JSTracer* trc) {
   // Discard code to avoid holding onto ExecutablePools.
   if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC()) {
     discardJitCode();
   }
 
@@ -951,19 +955,17 @@ bool RegExpShared::compile(JSContext* cx
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
   /* Parse the pattern. The RegExpCompileData is allocated in LifoAlloc and
    * will only be live while LifoAllocScope is on stack. */
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   irregexp::RegExpCompileData data;
   if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
-                              re->multiline(), mode == MatchOnly, re->unicode(),
-                              re->ignoreCase(), re->global(), re->sticky(),
-                              &data)) {
+                              mode == MatchOnly, re->getFlags(), &data)) {
     return false;
   }
 
   re->parenCount = data.capture_count;
 
   JitCodeTables tables;
   irregexp::RegExpCode code = irregexp::CompilePattern(
       cx, allocScope.alloc(), re, &data, input, false /* global() */,
@@ -1245,17 +1247,17 @@ void RegExpRealm::sweep() {
 
   if (optimizableRegExpInstanceShape_ &&
       IsAboutToBeFinalized(&optimizableRegExpInstanceShape_)) {
     optimizableRegExpInstanceShape_.set(nullptr);
   }
 }
 
 RegExpShared* RegExpZone::get(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags) {
+                              RegExpFlags flags) {
   DependentAddPtr<Set> p(cx, set_, Key(source, flags));
   if (p) {
     return *p;
   }
 
   auto shared = Allocate<RegExpShared>(cx);
   if (!shared) {
     return nullptr;
@@ -1267,17 +1269,17 @@ RegExpShared* RegExpZone::get(JSContext*
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
   return shared;
 }
 
 RegExpShared* RegExpZone::get(JSContext* cx, HandleAtom atom, JSString* opt) {
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (opt && !ParseRegExpFlags(cx, opt, &flags)) {
     return nullptr;
   }
 
   return get(cx, atom, flags);
 }
 
 size_t RegExpZone::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
@@ -1310,53 +1312,53 @@ JSObject* js::CloneRegExpObject(JSContex
   clone->initAndZeroLastIndex(shared->getSource(), shared->getFlags(), cx);
   clone->setShared(*shared);
 
   return clone;
 }
 
 template <typename CharT>
 static bool ParseRegExpFlags(const CharT* chars, size_t length,
-                             RegExpFlag* flagsOut, char16_t* invalidFlag) {
-  *flagsOut = RegExpFlag(0);
+                             RegExpFlags* flagsOut, char16_t* invalidFlag) {
+  *flagsOut = RegExpFlag::NoFlags;
 
   for (size_t i = 0; i < length; i++) {
-    RegExpFlag flag;
+    uint8_t flag;
     switch (chars[i]) {
+      case 'g':
+        flag = RegExpFlag::Global;
+        break;
       case 'i':
-        flag = IgnoreCaseFlag;
-        break;
-      case 'g':
-        flag = GlobalFlag;
+        flag = RegExpFlag::IgnoreCase;
         break;
       case 'm':
-        flag = MultilineFlag;
+        flag = RegExpFlag::Multiline;
+        break;
+      case 'u':
+        flag = RegExpFlag::Unicode;
         break;
       case 'y':
-        flag = StickyFlag;
-        break;
-      case 'u':
-        flag = UnicodeFlag;
+        flag = RegExpFlag::Sticky;
         break;
       default:
         *invalidFlag = chars[i];
         return false;
     }
     if (*flagsOut & flag) {
       *invalidFlag = chars[i];
       return false;
     }
-    *flagsOut = RegExpFlag(*flagsOut | flag);
+    *flagsOut |= flag;
   }
 
   return true;
 }
 
 bool js::ParseRegExpFlags(JSContext* cx, JSString* flagStr,
-                          RegExpFlag* flagsOut) {
+                          RegExpFlags* flagsOut) {
   JSLinearString* linear = flagStr->ensureLinear(cx);
   if (!linear) {
     return false;
   }
 
   size_t len = linear->length();
 
   bool ok;
@@ -1386,29 +1388,29 @@ bool js::ParseRegExpFlags(JSContext* cx,
 }
 
 template <XDRMode mode>
 XDRResult js::XDRScriptRegExpObject(XDRState<mode>* xdr,
                                     MutableHandle<RegExpObject*> objp) {
   /* NB: Keep this in sync with CloneScriptRegExpObject. */
 
   RootedAtom source(xdr->cx());
-  uint32_t flagsword = 0;
+  uint8_t flags = 0;
 
   if (mode == XDR_ENCODE) {
     MOZ_ASSERT(objp);
     RegExpObject& reobj = *objp;
     source = reobj.getSource();
-    flagsword = reobj.getFlags();
+    flags = reobj.getFlags().value();
   }
   MOZ_TRY(XDRAtom(xdr, &source));
-  MOZ_TRY(xdr->codeUint32(&flagsword));
+  MOZ_TRY(xdr->codeUint8(&flags));
   if (mode == XDR_DECODE) {
     RegExpObject* reobj = RegExpObject::create(
-        xdr->cx(), source, RegExpFlag(flagsword), TenuredObject);
+        xdr->cx(), source, RegExpFlags(flags), TenuredObject);
     if (!reobj) {
       return xdr->fail(JS::TranscodeResult_Throw);
     }
 
     objp.set(reobj);
   }
   return Ok();
 }
@@ -1438,37 +1440,36 @@ JS::ubi::Node::Size JS::ubi::Concrete<Re
   return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
          get().sizeOfExcludingThis(mallocSizeOf);
 }
 
 /*
  * Regular Expressions.
  */
 JS_PUBLIC_API JSObject* JS::NewRegExpObject(JSContext* cx, const char* bytes,
-                                            size_t length, unsigned flags) {
+                                            size_t length, RegExpFlags flags) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   UniqueTwoByteChars chars(InflateString(cx, bytes, length));
   if (!chars) {
     return nullptr;
   }
 
-  return RegExpObject::create(cx, chars.get(), length, RegExpFlag(flags),
-                              GenericObject);
+  return RegExpObject::create(cx, chars.get(), length, flags, GenericObject);
 }
 
 JS_PUBLIC_API JSObject* JS::NewUCRegExpObject(JSContext* cx,
                                               const char16_t* chars,
-                                              size_t length, unsigned flags) {
+                                              size_t length,
+                                              RegExpFlags flags) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
-  return RegExpObject::create(cx, chars, length, RegExpFlag(flags),
-                              GenericObject);
+  return RegExpObject::create(cx, chars, length, flags, GenericObject);
 }
 
 JS_PUBLIC_API bool JS::SetRegExpInput(JSContext* cx, HandleObject obj,
                                       HandleString input) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(input);
 
@@ -1543,23 +1544,23 @@ JS_PUBLIC_API bool JS::ObjectIsRegExp(JS
   if (!GetBuiltinClass(cx, obj, &cls)) {
     return false;
   }
 
   *isRegExp = cls == ESClass::RegExp;
   return true;
 }
 
-JS_PUBLIC_API unsigned JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
+JS_PUBLIC_API RegExpFlags JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   RegExpShared* shared = RegExpToShared(cx, obj);
   if (!shared) {
-    return false;
+    return RegExpFlag::NoFlags;
   }
   return shared->getFlags();
 }
 
 JS_PUBLIC_API JSString* JS::GetRegExpSource(JSContext* cx, HandleObject obj) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -10,16 +10,17 @@
 #define vm_RegExpObject_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "gc/Marking.h"
 #include "js/GCHashTable.h"
+#include "js/RegExpFlags.h"
 #include "proxy/Proxy.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpShared.h"
 #include "vm/Shape.h"
 
 /*
  * JavaScript Regular Expressions
@@ -65,29 +66,29 @@ class RegExpObject : public NativeObject
   static const Class protoClass_;
 
   // The maximum number of pairs a MatchResult can have, without having to
   // allocate a bigger MatchResult.
   static const size_t MaxPairCount = 14;
 
   template <typename CharT>
   static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
-                              RegExpFlag flags, NewObjectKind newKind);
+                              JS::RegExpFlags flags, NewObjectKind newKind);
 
   template <typename CharT>
   static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
-                              RegExpFlag flags,
+                              JS::RegExpFlags flags,
                               frontend::TokenStreamAnyChars& ts,
                               NewObjectKind kind);
 
   static RegExpObject* create(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags, NewObjectKind newKind);
+                              JS::RegExpFlags flags, NewObjectKind newKind);
 
   static RegExpObject* create(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags,
+                              JS::RegExpFlags flags,
                               frontend::TokenStreamAnyChars& ts,
                               NewObjectKind newKind);
 
   /*
    * Compute the initial shape to associate with fresh RegExp objects,
    * encoding their initial properties. Return the shape after
    * changing |obj|'s last property to it.
    */
@@ -126,28 +127,30 @@ class RegExpObject : public NativeObject
   }
 
   void setSource(JSAtom* source) { setSlot(SOURCE_SLOT, StringValue(source)); }
 
   /* Flags. */
 
   static unsigned flagsSlot() { return FLAGS_SLOT; }
 
-  RegExpFlag getFlags() const {
-    return RegExpFlag(getFixedSlot(FLAGS_SLOT).toInt32());
+  JS::RegExpFlags getFlags() const {
+    return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
   }
-  void setFlags(RegExpFlag flags) { setSlot(FLAGS_SLOT, Int32Value(flags)); }
+  void setFlags(JS::RegExpFlags flags) {
+    setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
+  }
 
-  bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; }
-  bool global() const { return getFlags() & GlobalFlag; }
-  bool multiline() const { return getFlags() & MultilineFlag; }
-  bool sticky() const { return getFlags() & StickyFlag; }
-  bool unicode() const { return getFlags() & UnicodeFlag; }
+  bool global() const { return getFlags().global(); }
+  bool ignoreCase() const { return getFlags().ignoreCase(); }
+  bool multiline() const { return getFlags().multiline(); }
+  bool unicode() const { return getFlags().unicode(); }
+  bool sticky() const { return getFlags().sticky(); }
 
-  static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
+  static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);
 
   static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);
 
   bool hasShared() { return !!sharedRef(); }
 
   void setShared(RegExpShared& shared) {
     MOZ_ASSERT(!hasShared());
     sharedRef().init(&shared);
@@ -156,22 +159,23 @@ class RegExpObject : public NativeObject
   HeapPtrRegExpShared& sharedRef() {
     auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
     return reinterpret_cast<HeapPtrRegExpShared&>(ref);
   }
 
   static void trace(JSTracer* trc, JSObject* obj);
   void trace(JSTracer* trc);
 
-  void initIgnoringLastIndex(JSAtom* source, RegExpFlag flags);
+  void initIgnoringLastIndex(JSAtom* source, JS::RegExpFlags flags);
 
   // NOTE: This method is *only* safe to call on RegExps that haven't been
   //       exposed to script, because it requires that the "lastIndex"
   //       property be writable.
-  void initAndZeroLastIndex(JSAtom* source, RegExpFlag flags, JSContext* cx);
+  void initAndZeroLastIndex(JSAtom* source, JS::RegExpFlags flags,
+                            JSContext* cx);
 
 #ifdef DEBUG
   static MOZ_MUST_USE bool dumpBytecode(JSContext* cx,
                                         Handle<RegExpObject*> regexp,
                                         bool match_only,
                                         HandleLinearString input);
 #endif
 
@@ -188,17 +192,18 @@ class RegExpObject : public NativeObject
 };
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
-bool ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
+bool ParseRegExpFlags(JSContext* cx, JSString* flagStr,
+                      JS::RegExpFlags* flagsOut);
 
 // Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for
 // obj.
 inline RegExpShared* RegExpToShared(JSContext* cx, HandleObject obj) {
   if (obj->is<RegExpObject>()) {
     return RegExpObject::getShared(cx, obj.as<RegExpObject>());
   }
 
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -16,16 +16,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "gc/Zone.h"
 #include "js/AllocPolicy.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/UbiNode.h"
 #include "js/Vector.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSAtom.h"
 #include "vm/RegExpConstants.h"
 
 namespace js {
 
@@ -78,17 +79,17 @@ class RegExpShared : public gc::TenuredC
     bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
       return byteCode || (force == DontForceByteCode && jitCode);
     }
   };
 
   /* Source to the RegExp, for lazy compilation. */
   GCPtr<JSAtom*> source;
 
-  RegExpFlag flags;
+  JS::RegExpFlags flags;
   bool canStringMatch;
   size_t parenCount;
 
   RegExpCompilation compilationArray[4];
 
   static int CompilationIndex(CompilationMode mode, bool latin1) {
     switch (mode) {
       case Normal:
@@ -98,17 +99,17 @@ class RegExpShared : public gc::TenuredC
     }
     MOZ_CRASH();
   }
 
   // Tables referenced by JIT code.
   JitCodeTables tables;
 
   /* Internal functions. */
-  RegExpShared(JSAtom* source, RegExpFlag flags);
+  RegExpShared(JSAtom* source, JS::RegExpFlags flags);
 
   static bool compile(JSContext* cx, MutableHandleRegExpShared res,
                       HandleLinearString input, CompilationMode mode,
                       ForceByteCodeEnum force);
   static bool compile(JSContext* cx, MutableHandleRegExpShared res,
                       HandleAtom pattern, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force);
 
@@ -143,22 +144,23 @@ class RegExpShared : public gc::TenuredC
     MOZ_ASSERT(isCompiled());
     return parenCount;
   }
 
   /* Accounts for the "0" (whole match) pair. */
   size_t pairCount() const { return getParenCount() + 1; }
 
   JSAtom* getSource() const { return source; }
-  RegExpFlag getFlags() const { return flags; }
-  bool ignoreCase() const { return flags & IgnoreCaseFlag; }
-  bool global() const { return flags & GlobalFlag; }
-  bool multiline() const { return flags & MultilineFlag; }
-  bool sticky() const { return flags & StickyFlag; }
-  bool unicode() const { return flags & UnicodeFlag; }
+  JS::RegExpFlags getFlags() const { return flags; }
+
+  bool global() const { return flags.global(); }
+  bool ignoreCase() const { return flags.ignoreCase(); }
+  bool multiline() const { return flags.multiline(); }
+  bool unicode() const { return flags.unicode(); }
+  bool sticky() const { return flags.sticky(); }
 
   bool isCompiled(CompilationMode mode, bool latin1,
                   ForceByteCodeEnum force = DontForceByteCode) const {
     return compilation(mode, latin1).compiled(force);
   }
   bool isCompiled() const {
     return isCompiled(Normal, true) || isCompiled(Normal, false) ||
            isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
@@ -192,32 +194,32 @@ class RegExpShared : public gc::TenuredC
 #ifdef DEBUG
   static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res,
                            bool match_only, HandleLinearString input);
 #endif
 };
 
 class RegExpZone {
   struct Key {
-    JSAtom* atom;
-    uint16_t flag;
+    JSAtom* atom = nullptr;
+    JS::RegExpFlags flags = JS::RegExpFlag::NoFlags;
 
-    Key() : atom(nullptr), flag(0) {}
-    Key(JSAtom* atom, RegExpFlag flag) : atom(atom), flag(flag) {}
+    Key() = default;
+    Key(JSAtom* atom, JS::RegExpFlags flags) : atom(atom), flags(flags) {}
     MOZ_IMPLICIT Key(const ReadBarriered<RegExpShared*>& shared)
         : atom(shared.unbarrieredGet()->getSource()),
-          flag(shared.unbarrieredGet()->getFlags()) {}
+          flags(shared.unbarrieredGet()->getFlags()) {}
 
     typedef Key Lookup;
     static HashNumber hash(const Lookup& l) {
       HashNumber hash = DefaultHasher<JSAtom*>::hash(l.atom);
-      return mozilla::AddToHash(hash, l.flag);
+      return mozilla::AddToHash(hash, l.flags.value());
     }
     static bool match(Key l, Key r) {
-      return l.atom == r.atom && l.flag == r.flag;
+      return l.atom == r.atom && l.flags == r.flags;
     }
   };
 
   /*
    * The set of all RegExpShareds in the zone. On every GC, every RegExpShared
    * that was not marked is deleted and removed from the set.
    */
   using Set = JS::WeakCache<
@@ -226,22 +228,22 @@ class RegExpZone {
 
  public:
   explicit RegExpZone(Zone* zone);
 
   ~RegExpZone() { MOZ_ASSERT(set_.empty()); }
 
   bool empty() const { return set_.empty(); }
 
-  RegExpShared* maybeGet(JSAtom* source, RegExpFlag flags) const {
+  RegExpShared* maybeGet(JSAtom* source, JS::RegExpFlags flags) const {
     Set::Ptr p = set_.lookup(Key(source, flags));
     return p ? *p : nullptr;
   }
 
-  RegExpShared* get(JSContext* cx, HandleAtom source, RegExpFlag flags);
+  RegExpShared* get(JSContext* cx, HandleAtom source, JS::RegExpFlags flags);
 
   /* Like 'get', but compile 'maybeOpt' (if non-null). */
   RegExpShared* get(JSContext* cx, HandleAtom source, JSString* maybeOpt);
 
 #ifdef DEBUG
   void clear() { set_.clear(); }
 #endif
 
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -24,17 +24,17 @@ class RegExpStatics {
   HeapPtr<JSLinearString*> matchesInput;
 
   /*
    * The previous RegExp input, used to resolve lazy state.
    * A raw RegExpShared cannot be stored because it may be in
    * a different compartment via evalcx().
    */
   HeapPtr<JSAtom*> lazySource;
-  RegExpFlag lazyFlags;
+  JS::RegExpFlags lazyFlags;
   size_t lazyIndex;
 
   /* The latest RegExp input, set before execution. */
   HeapPtr<JSString*> pendingInput;
 
   /*
    * If non-zero, |matchesInput| and the |lazy*| fields may be used
    * to replay the last executed RegExp, and |matches| is invalid.
@@ -269,17 +269,17 @@ inline bool RegExpStatics::updateFromMat
 
   return true;
 }
 
 inline void RegExpStatics::clear() {
   matches.forgetArray();
   matchesInput = nullptr;
   lazySource = nullptr;
-  lazyFlags = RegExpFlag(0);
+  lazyFlags = JS::RegExpFlag::NoFlags;
   lazyIndex = size_t(-1);
   pendingInput = nullptr;
   pendingLazyEvaluation = false;
 }
 
 inline void RegExpStatics::setPendingInput(JSString* newInput) {
   pendingInput = newInput;
 }
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -23,16 +23,17 @@
  *     We serialize objects that appear in multiple places in the input as
  *     backreferences, using sequential integer indexes.
  *     See `JSStructuredCloneReader::allObjs`, our take on the "memory" map
  *     in the spec's StructuredDeserialize.
  */
 
 #include "js/StructuredClone.h"
 
+#include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Unused.h"
 
 #include <algorithm>
 #include <memory>
@@ -41,16 +42,17 @@
 #include "jsapi.h"
 #include "jsdate.h"
 
 #include "builtin/DataViewObject.h"
 #include "builtin/MapObject.h"
 #include "js/ArrayBuffer.h"  // JS::{ArrayBufferHasData,DetachArrayBuffer,IsArrayBufferObject,New{,Mapped}ArrayBufferWithContents,ReleaseMappedArrayBufferContents}
 #include "js/Date.h"
 #include "js/GCHashTable.h"
+#include "js/RegExpFlags.h"        // JS::RegExpFlags
 #include "js/SharedArrayBuffer.h"  // JS::IsSharedArrayBufferObject
 #include "js/Wrapper.h"
 #include "vm/BigIntType.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 #include "vm/SavedFrame.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
@@ -59,17 +61,19 @@
 
 #include "vm/InlineCharBuffer-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using JS::CanonicalizeNaN;
+using JS::RegExpFlags;
 using JS::RootedValueVector;
+using mozilla::AssertedCast;
 using mozilla::BitwiseCast;
 using mozilla::NativeEndian;
 using mozilla::NumbersAreIdentical;
 using mozilla::RangedPtr;
 
 // When you make updates here, make sure you consider whether you need to bump
 // the value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h.  You
 // will likely need to increment the version if anything at all changes in the
@@ -1664,17 +1668,17 @@ bool JSStructuredCloneWriter::startWrite
         }
         return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
       }
       case ESClass::RegExp: {
         RegExpShared* re = RegExpToShared(context(), obj);
         if (!re) {
           return false;
         }
-        return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
+        return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags().value()) &&
                writeString(SCTAG_STRING, re->getSource());
       }
       case ESClass::ArrayBuffer: {
         if (JS::IsArrayBufferObject(obj) && JS::ArrayBufferHasData(obj)) {
           return writeArrayBuffer(obj);
         }
         break;
       }
@@ -2443,17 +2447,17 @@ bool JSStructuredCloneReader::startRead(
       if (!obj) {
         return false;
       }
       vp.setObject(*obj);
       break;
     }
 
     case SCTAG_REGEXP_OBJECT: {
-      RegExpFlag flags = RegExpFlag(data);
+      RegExpFlags flags = AssertedCast<uint8_t>(data);
       uint32_t tag2, stringData;
       if (!in.readPair(&tag2, &stringData)) {
         return false;
       }
       if (tag2 != SCTAG_STRING) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
                                   JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
         return false;
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2657,18 +2657,24 @@ void TypeZone::processPendingRecompiles(
   MOZ_ASSERT(recompiles.empty());
 }
 
 void TypeZone::addPendingRecompile(JSContext* cx, const RecompileInfo& info) {
   InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%u", info.script(),
             info.script()->filename(), info.script()->lineno());
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
-  if (!cx->zone()->types.activeAnalysis->pendingRecompiles.append(info)) {
-    oomUnsafe.crash("Could not update pendingRecompiles");
+  RecompileInfoVector& vector =
+      cx->zone()->types.activeAnalysis->pendingRecompiles;
+  if (!vector.append(info)) {
+    // BUG 1536159: For diagnostics, compute the size of the failed allocation.
+    // This presumes the vector growth strategy is to double. This is only used
+    // for crash reporting so not a problem if we get it wrong.
+    size_t allocSize = 2 * sizeof(RecompileInfo) * vector.capacity();
+    oomUnsafe.crash(allocSize, "Could not update pendingRecompiles");
   }
 }
 
 void TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) {
   MOZ_ASSERT(script);
 
   CancelOffThreadIonCompile(script);
 
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -235,17 +235,18 @@ already_AddRefed<XPCNativeInterface> XPC
 
       nsAutoString filename;
       uint32_t lineno = 0, column = 0;
       nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
       nsCOMPtr<nsIScriptError> error(
           do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
       error->Init(NS_ConvertUTF8toUTF16(errorMsg), filename, EmptyString(),
                   lineno, column, nsIScriptError::warningFlag,
-                  "chrome javascript", false /* from private window */);
+                  "chrome javascript", false /* from private window */,
+                  true /* from chrome context */);
       console->LogMessage(error);
     }
   }
 
   uint16_t methodCount = aInfo->MethodCount();
   uint16_t constCount = aInfo->ConstantCount();
 
   // If the interface does not have nsISupports in its inheritance chain
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -10202,17 +10202,18 @@ void ReflowCountMgr::PaintCount(const ch
       gfxPoint devPixelOffset =
           nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
       aRenderingContext->SetMatrixDouble(
           aRenderingContext->CurrentMatrixDouble().PreTranslate(
               devPixelOffset));
 
       // We don't care about the document language or user fonts here;
       // just get a default Latin font.
-      nsFont font(eFamily_serif, nsPresContext::CSSPixelsToAppUnits(11));
+      nsFont font(StyleGenericFontFamily::Serif,
+                  nsPresContext::CSSPixelsToAppUnits(11));
       nsFontMetrics::Params params;
       params.language = nsGkAtoms::x_western;
       params.textPerf = aPresContext->GetTextPerfMetrics();
       params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
       RefPtr<nsFontMetrics> fm =
           aPresContext->DeviceContext()->GetMetricsFor(font, params);
 
       char buf[16];
--- a/layout/base/StaticPresData.cpp
+++ b/layout/base/StaticPresData.cpp
@@ -35,29 +35,27 @@ StaticPresData::StaticPresData() {
 
 #define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
   _pref.Assign(_s0);                        \
   _pref.Append(_s1);
 
 // clang-format off
 static const char* const kGenericFont[] = {
   ".variable.",
-  ".fixed.",
   ".serif.",
   ".sans-serif.",
   ".monospace.",
   ".cursive.",
   ".fantasy."
 };
 // clang-format on
 
 // These are private, use the list in nsFont.h if you want a public list.
 enum {
   eDefaultFont_Variable,
-  eDefaultFont_Fixed,
   eDefaultFont_Serif,
   eDefaultFont_SansSerif,
   eDefaultFont_Monospace,
   eDefaultFont_Cursive,
   eDefaultFont_Fantasy,
   eDefaultFont_COUNT
 };
 
@@ -88,31 +86,30 @@ void LangGroupFontPrefs::Initialize(nsSt
   font.minimum-size.[langGroup] = integer
     settable by the user
   */
 
   nsAutoCString langGroup;
   aLangGroupAtom->ToUTF8String(langGroup);
 
   mDefaultVariableFont.size = nsPresContext::CSSPixelsToAppUnits(16);
-  mDefaultFixedFont.size = nsPresContext::CSSPixelsToAppUnits(13);
+  mDefaultMonospaceFont.size = nsPresContext::CSSPixelsToAppUnits(13);
 
   nsAutoCString pref;
 
   // get font.minimum-size.[langGroup]
 
   MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup);
 
   int32_t size = Preferences::GetInt(pref.get());
   mMinimumFontSize = nsPresContext::CSSPixelsToAppUnits(size);
 
   // clang-format off
   nsFont* fontTypes[] = {
     &mDefaultVariableFont,
-    &mDefaultFixedFont,
     &mDefaultSerifFont,
     &mDefaultSansSerifFont,
     &mDefaultMonospaceFont,
     &mDefaultCursiveFont,
     &mDefaultFantasyFont
   };
   // clang-format on
   static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT,
@@ -136,50 +133,43 @@ void LangGroupFontPrefs::Initialize(nsSt
     if (eType == eDefaultFont_Variable) {
       // XXX "font.name.variable."?  There is no such pref...
       MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup);
 
       nsAutoCString value;
       Preferences::GetCString(pref.get(), value);
       if (!value.IsEmpty()) {
         FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
-        FontFamilyType defaultType = defaultVariableName.mType;
-        NS_ASSERTION(
-            defaultType == eFamily_serif || defaultType == eFamily_sans_serif,
-            "default type must be serif or sans-serif");
+        StyleGenericFontFamily defaultType = defaultVariableName.mGeneric;
+        NS_ASSERTION(defaultType == StyleGenericFontFamily::Serif ||
+                         defaultType == StyleGenericFontFamily::SansSerif,
+                     "default type must be serif or sans-serif");
         mDefaultVariableFont.fontlist = FontFamilyList();
         mDefaultVariableFont.fontlist.SetDefaultFontType(defaultType);
         // We create mDefaultVariableFont.fontlist with defaultType as the
         // fallback font, and not as part of the font list proper. This way,
         // it can be overwritten should there be a language change.
       } else {
         MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
         Preferences::GetCString(pref.get(), value);
         if (!value.IsEmpty()) {
           FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
-          FontFamilyType defaultType = defaultVariableName.mType;
-          NS_ASSERTION(
-              defaultType == eFamily_serif || defaultType == eFamily_sans_serif,
-              "default type must be serif or sans-serif");
+          StyleGenericFontFamily defaultType = defaultVariableName.mGeneric;
+          NS_ASSERTION(defaultType == StyleGenericFontFamily::Serif ||
+                           defaultType == StyleGenericFontFamily::SansSerif,
+                       "default type must be serif or sans-serif");
           mDefaultVariableFont.fontlist = FontFamilyList();
           mDefaultVariableFont.fontlist.SetDefaultFontType(defaultType);
           // We create mDefaultVariableFont.fontlist with defaultType as the
           // (fallback) font, and not as part of the font list proper. This way,
           // it can be overwritten should there be a language change.
         }
       }
     } else {
-      if (eType == eDefaultFont_Monospace) {
-        // This takes care of the confusion whereby people often expect
-        // "monospace" to have the same default font-size as "-moz-fixed" (this
-        // tentative size may be overwritten with the specific value for
-        // "monospace" when "font.size.monospace.[langGroup]" is read -- see
-        // below)
-        mDefaultMonospaceFont.size = mDefaultFixedFont.size;
-      } else if (eType != eDefaultFont_Fixed) {
+      if (eType != eDefaultFont_Monospace) {
         // all the other generic fonts are initialized with the size of the
         // variable font, but their specific size can supersede later -- see
         // below
         font->size = mDefaultVariableFont.size;
       }
     }
 
     // Bug 84398: for spec purists, a different font-size only applies to the
--- a/layout/base/StaticPresData.h
+++ b/layout/base/StaticPresData.h
@@ -17,23 +17,23 @@
 namespace mozilla {
 
 struct LangGroupFontPrefs {
   // Font sizes default to zero; they will be set in GetFontPreferences
   LangGroupFontPrefs()
       : mLangGroup(nullptr),
         mMinimumFontSize(0),
         mDefaultVariableFont(),
-        mDefaultFixedFont(mozilla::eFamily_monospace, 0),
-        mDefaultSerifFont(mozilla::eFamily_serif, 0),
-        mDefaultSansSerifFont(mozilla::eFamily_sans_serif, 0),
-        mDefaultMonospaceFont(mozilla::eFamily_monospace, 0),
-        mDefaultCursiveFont(mozilla::eFamily_cursive, 0),
-        mDefaultFantasyFont(mozilla::eFamily_fantasy, 0) {
-    mDefaultVariableFont.fontlist.SetDefaultFontType(mozilla::eFamily_serif);
+        mDefaultSerifFont(StyleGenericFontFamily::Serif, 0),
+        mDefaultSansSerifFont(StyleGenericFontFamily::SansSerif, 0),
+        mDefaultMonospaceFont(StyleGenericFontFamily::Monospace, 0),
+        mDefaultCursiveFont(StyleGenericFontFamily::Cursive, 0),
+        mDefaultFantasyFont(StyleGenericFontFamily::Fantasy, 0) {
+    mDefaultVariableFont.fontlist.SetDefaultFontType(
+        StyleGenericFontFamily::Serif);
     // We create mDefaultVariableFont.fontlist with defaultType as the
     // fallback font, and not as part of the font list proper. This way,
     // it can be overwritten should there be a language change.
   }
 
   void Reset() {
     // Throw away any other LangGroupFontPrefs objects:
     mNext = nullptr;
@@ -62,58 +62,47 @@ struct LangGroupFontPrefs {
   void Initialize(nsStaticAtom* aLangGroupAtom);
 
   /**
    * Get the default font for the given language and generic font ID.
    * aLanguage may not be nullptr.
    *
    * This object is read-only, you must copy the font to modify it.
    *
-   * When aFontID is kPresContext_DefaultVariableFontID or
-   * kPresContext_DefaultFixedFontID (which equals
-   * kGenericFont_moz_fixed, which is used for the -moz-fixed generic),
-   * the nsFont returned has its name as a CSS generic family (serif or
-   * sans-serif for the former, monospace for the latter), and its size
-   * as the default font size for variable or fixed fonts for the
-   * language group.
-   *
    * For aFontID corresponding to a CSS Generic, the nsFont returned has
    * its name set to that generic font's name, and its size set to
    * the user's preference for font size for that generic and the
    * given language.
    */
-  const nsFont* GetDefaultFont(uint8_t aFontID) const {
-    switch (aFontID) {
+  const nsFont* GetDefaultFont(StyleGenericFontFamily aFamily) const {
+    switch (aFamily) {
       // Special (our default variable width font and fixed width font)
-      case kGenericFont_moz_variable:
+      case StyleGenericFontFamily::None:
         return &mDefaultVariableFont;
-      case kGenericFont_moz_fixed:
-        return &mDefaultFixedFont;
       // CSS
-      case kGenericFont_serif:
+      case StyleGenericFontFamily::Serif:
         return &mDefaultSerifFont;
-      case kGenericFont_sans_serif:
+      case StyleGenericFontFamily::SansSerif:
         return &mDefaultSansSerifFont;
-      case kGenericFont_monospace:
+      case StyleGenericFontFamily::Monospace:
         return &mDefaultMonospaceFont;
-      case kGenericFont_cursive:
+      case StyleGenericFontFamily::Cursive:
         return &mDefaultCursiveFont;
-      case kGenericFont_fantasy:
+      case StyleGenericFontFamily::Fantasy:
         return &mDefaultFantasyFont;
         break;
       default:
         MOZ_ASSERT_UNREACHABLE("invalid font id");
         return nullptr;
     }
   }
 
   nsStaticAtom* mLangGroup;
   nscoord mMinimumFontSize;
   nsFont mDefaultVariableFont;
-  nsFont mDefaultFixedFont;
   nsFont mDefaultSerifFont;
   nsFont mDefaultSansSerifFont;
   nsFont mDefaultMonospaceFont;
   nsFont mDefaultCursiveFont;
   nsFont mDefaultFantasyFont;
   mozilla::UniquePtr<LangGroupFontPrefs> mNext;
 };
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -10578,17 +10578,18 @@ void nsCSSFrameConstructor::ConstructBlo
   //
   // ColumnSet linkage described in 3): B -> G -> Q
   //
   // Block linkage described in 4): C -> H -> R  and  I -> S
   //
   // clang-format on
 
   nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame);
-  MOZ_ASSERT(blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame(),
+  MOZ_ASSERT(blockFrame &&
+                 (blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame()),
              "not a block frame nor a details frame?");
 
   // Create column hierarchy if necessary.
   const bool needsColumn =
       aComputedStyle->StyleColumn()->IsColumnContainerStyle();
   if (needsColumn) {
     *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame,
                                       blockFrame, aComputedStyle);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9744,18 +9744,19 @@ already_AddRefed<nsFontMetrics> nsLayout
 void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
                                       LookAndFeel::FontID aFontID,
                                       const nsFont* aDefaultVariableFont) {
   gfxFontStyle fontStyle;
   nsAutoString systemFontName;
   if (LookAndFeel::GetFont(aFontID, systemFontName, fontStyle)) {
     systemFontName.Trim("\"'");
     aSystemFont->fontlist =
-        FontFamilyList(NS_ConvertUTF16toUTF8(systemFontName), eUnquotedName);
-    aSystemFont->fontlist.SetDefaultFontType(eFamily_none);
+        FontFamilyList(NS_ConvertUTF16toUTF8(systemFontName),
+                       StyleFontFamilyNameSyntax::Identifiers);
+    aSystemFont->fontlist.SetDefaultFontType(StyleGenericFontFamily::None);
     aSystemFont->style = fontStyle.style;
     aSystemFont->systemFont = fontStyle.systemFont;
     aSystemFont->weight = fontStyle.weight;
     aSystemFont->stretch = fontStyle.stretch;
     aSystemFont->size = CSSPixel::ToAppUnits(fontStyle.size);
     // aSystemFont->langGroup = fontStyle.langGroup;
     aSystemFont->sizeAdjust = fontStyle.sizeAdjust;
 
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -850,21 +850,18 @@ nsIFrame::LogicalSides nsInlineFrame::Ge
 
 nscoord nsInlineFrame::GetLogicalBaseline(
     mozilla::WritingMode aWritingMode) const {
   return mBaseline;
 }
 
 #ifdef ACCESSIBILITY
 a11y::AccType nsInlineFrame::AccessibleType() {
-  // Broken image accessibles are created here, because layout
-  // replaces the image or image control frame with an inline frame
-  if (mContent->IsHTMLElement(
-          nsGkAtoms::input))  // Broken <input type=image ... />
-    return a11y::eHTMLButtonType;
+  // FIXME(emilio): This is broken, if the image has its default `display` value
+  // overridden. Should be somewhere else.
   if (mContent->IsHTMLElement(
           nsGkAtoms::img))  // Create accessible for broken <img>
     return a11y::eHyperTextType;
 
   return a11y::eNoType;
 }
 #endif
 
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -48,23 +48,22 @@ nsSimplePageSequenceFrame::nsSimplePageS
     ComputedStyle* aStyle, nsPresContext* aPresContext)
     : nsContainerFrame(aStyle, aPresContext, kClassID),
       mTotalPages(-1),
       mCalledBeginPage(false),
       mCurrentCanvasListSetup(false) {
   nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
   mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
 
-  // XXX Unsafe to assume successful allocation
   mPageData = new nsSharedPageData();
   mPageData->mHeadFootFont =
       *PresContext()
            ->Document()
            ->GetFontPrefsForLang(aStyle->StyleFont()->mLanguage)
-           ->GetDefaultFont(kGenericFont_serif);
+           ->GetDefaultFont(StyleGenericFontFamily::Serif);
   mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10);
 
   // Doing this here so we only have to go get these formats once
   SetPageNumberFormat("pagenumber", "%1$d", true);
   SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
 }
 
 nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame() {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5726,17 +5726,17 @@ gfxFloat nsTextFrame::ComputeSelectionUn
       // metrics.  It should be constant pixels value which is decided from the
       // default font size.  Note that if the actual font size is smaller than
       // the default font size, we should use the actual font size because the
       // computed value from the default font size can be too thick for the
       // current font size.
       nscoord defaultFontSize =
           aPresContext->Document()
               ->GetFontPrefsForLang(nullptr)
-              ->GetDefaultFont(kPresContext_DefaultVariableFont_ID)
+              ->GetDefaultFont(StyleGenericFontFamily::None)
               ->size;
       int32_t zoomedFontSize = aPresContext->AppUnitsToDevPixels(
           nsStyleFont::ZoomText(*aPresContext->Document(), defaultFontSize));
       gfxFloat fontSize =
           std::min(gfxFloat(zoomedFontSize), aFontMetrics.emHeight);
       fontSize = std::max(fontSize, 1.0);
       return ceil(fontSize / 20);
     }
--- a/layout/inspector/InspectorFontFace.cpp
+++ b/layout/inspector/InspectorFontFace.cpp
@@ -26,45 +26,43 @@ InspectorFontFace::InspectorFontFace(gfx
                                      FontMatchType aMatchType)
     : mFontEntry(aFontEntry), mFontGroup(aFontGroup), mMatchType(aMatchType) {
   MOZ_COUNT_CTOR(InspectorFontFace);
 }
 
 InspectorFontFace::~InspectorFontFace() { MOZ_COUNT_DTOR(InspectorFontFace); }
 
 bool InspectorFontFace::FromFontGroup() {
-  return bool(mMatchType & FontMatchType::kFontGroup);
+  return bool(mMatchType.kind & FontMatchType::Kind::kFontGroup);
 }
 
 bool InspectorFontFace::FromLanguagePrefs() {
-  return bool(mMatchType & FontMatchType::kPrefsFallback);
+  return bool(mMatchType.kind & FontMatchType::Kind::kPrefsFallback);
 }
 
 bool InspectorFontFace::FromSystemFallback() {
-  return bool(mMatchType & FontMatchType::kSystemFallback);
+  return bool(mMatchType.kind & FontMatchType::Kind::kSystemFallback);
 }
 
 void InspectorFontFace::GetName(nsAString& aName) {
   if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
     aName.Append(NS_ConvertUTF8toUTF16(mFontEntry->mUserFontData->mRealName));
   } else {
     aName.Append(NS_ConvertUTF8toUTF16(mFontEntry->RealFaceName()));
   }
 }
 
 void InspectorFontFace::GetCSSFamilyName(nsAString& aCSSFamilyName) {
   aCSSFamilyName.Append(NS_ConvertUTF8toUTF16(mFontEntry->FamilyName()));
 }
 
 void InspectorFontFace::GetCSSGeneric(nsAString& aName) {
-  auto genericType = FontFamilyType(mMatchType & FontMatchType::kGenericMask);
-  if (genericType >= FontFamilyType::eFamily_generic_first &&
-      genericType <= FontFamilyType::eFamily_generic_last) {
-    aName.AssignASCII(gfxPlatformFontList::GetGenericName(genericType));
+  if (mMatchType.generic != StyleGenericFontFamily::None) {
+    aName.AssignASCII(gfxPlatformFontList::GetGenericName(mMatchType.generic));
   } else {
     aName.Truncate(0);
   }
 }
 
 CSSFontFaceRule* InspectorFontFace::GetRule() {
   if (!mRule) {
     // check whether this font entry is associated with an @font-face rule
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -51,23 +51,23 @@ using namespace mozilla::image;
 // BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
 // are installed. "kMaxScaleFactor" is required to limit the scale for the
 // vertical and horizontal stretchy operators.
 static const float kMaxScaleFactor = 20.0;
 static const float kLargeOpFactor = float(M_SQRT2);
 static const float kIntegralFactor = 2.0;
 
 static void NormalizeDefaultFont(nsFont& aFont, float aFontSizeInflation) {
-  if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
+  if (aFont.fontlist.GetDefaultFontType() != StyleGenericFontFamily::None) {
     nsTArray<FontFamilyName> names;
     names.AppendElements(aFont.fontlist.GetFontlist()->mNames);
     names.AppendElement(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
 
     aFont.fontlist.SetFontlist(std::move(names));
-    aFont.fontlist.SetDefaultFontType(eFamily_none);
+    aFont.fontlist.SetDefaultFontType(StyleGenericFontFamily::None);
   }
   aFont.size = NSToCoordRound(aFont.size * aFontSizeInflation);
 }
 
 // -----------------------------------------------------------------------------
 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 
 // -----------------------------------------------------------------------------
@@ -161,18 +161,18 @@ static nsresult LoadProperties(const nsA
                                                 uriStr);
 }
 
 class nsPropertiesTable final : public nsGlyphTable {
  public:
   explicit nsPropertiesTable(const nsACString& aPrimaryFontName)
       : mState(NS_TABLE_STATE_EMPTY) {
     MOZ_COUNT_CTOR(nsPropertiesTable);
-    mGlyphCodeFonts.AppendElement(
-        FontFamilyName(aPrimaryFontName, eUnquotedName));
+    mGlyphCodeFonts.AppendElement(FontFamilyName(
+        aPrimaryFontName, StyleFontFamilyNameSyntax::Identifiers));
   }
 
   ~nsPropertiesTable() { MOZ_COUNT_DTOR(nsPropertiesTable); }
 
   const FontFamilyName& PrimaryFontName() const { return mGlyphCodeFonts[0]; }
 
   const FontFamilyName& FontNameFor(
       const nsGlyphCode& aGlyphCode) const override {
@@ -281,17 +281,18 @@ nsGlyphCode nsPropertiesTable::ElementAt
     for (int32_t i = 1;; i++) {
       key.AssignLiteral("external.");
       key.AppendInt(i, 10);
       rv = mGlyphProperties->GetStringProperty(key, value);
       if (NS_FAILED(rv)) break;
       Clean(value);
       mGlyphCodeFonts.AppendElement(FontFamilyName(
           NS_ConvertUTF16toUTF8(value),
-          eUnquotedName));  // i.e., mGlyphCodeFonts[i] holds this font name
+          StyleFontFamilyNameSyntax::Identifiers));  // i.e., mGlyphCodeFonts[i]
+                                                     // holds this font name
     }
   }
 
   // Update our cache if it is not associated to this character
   if (mCharCache != aChar) {
     // The key in the property file is interpreted as ASCII and kept
     // as such ...
     char key[10];
@@ -414,17 +415,18 @@ class nsOpenTypeTable final : public nsG
 
  private:
   RefPtr<gfxFont> mFont;
   FontFamilyName mFontFamilyName;
   uint32_t mGlyphID;
 
   explicit nsOpenTypeTable(gfxFont* aFont)
       : mFont(aFont),
-        mFontFamilyName(aFont->GetFontEntry()->FamilyName(), eUnquotedName),
+        mFontFamilyName(aFont->GetFontEntry()->FamilyName(),
+                        StyleFontFamilyNameSyntax::Identifiers),
         mGlyphID(0) {
     MOZ_COUNT_CTOR(nsOpenTypeTable);
   }
 
   void UpdateCache(DrawTarget* aDrawTarget, int32_t aAppUnitsPerDevPixel,
                    gfxFontGroup* aFontGroup, char16_t aChar);
 };
 
@@ -511,17 +513,17 @@ already_AddRefed<gfxTextRun> nsOpenTypeT
                "nsOpenTypeTable can only access glyphs by id");
 
   gfxTextRunFactory::Parameters params = {
       aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel};
   RefPtr<gfxTextRun> textRun =
       gfxTextRun::Create(&params, 1, aFontGroup, gfx::ShapedTextFlags(),
                          nsTextFrameUtils::Flags());
   textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
-                       FontMatchType::kFontGroup, 0, false,
+                       FontMatchType::Kind::kFontGroup, 0, false,
                        gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL);
   // We don't care about CSS writing mode here;
   // math runs are assumed to be horizontal.
   gfxTextRun::DetailedGlyph detailedGlyph;
   detailedGlyph.mGlyphID = aGlyph.glyphID;
   detailedGlyph.mAdvance = NSToCoordRound(
       aAppUnitsPerDevPixel * aFontGroup->GetFirstValidFont()->GetGlyphHAdvance(
                                  aDrawTarget, aGlyph.glyphID));
@@ -885,17 +887,17 @@ bool nsMathMLChar::SetFontFamily(nsPresC
     params.textPerf = aPresContext->GetTextPerfMetrics();
     params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
     RefPtr<nsFontMetrics> fm =
         aPresContext->DeviceContext()->GetMetricsFor(font, params);
     // Set the font if it is an unicode table
     // or if the same family name has been found
     gfxFont* firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
     FontFamilyList firstFontList(firstFont->GetFontEntry()->FamilyName(),
-                                 eUnquotedName);
+                                 StyleFontFamilyNameSyntax::Identifiers);
     if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
         firstFontList == familyList) {
       aFont.fontlist = familyList;
       *aFontGroup = fm->GetThebesFontGroup();
     } else {
       return false;  // We did not set the font
     }
   }
@@ -1275,27 +1277,27 @@ bool nsMathMLChar::StretchEnumContext::T
 
 // This is called for each family, whether it exists or not
 bool nsMathMLChar::StretchEnumContext::EnumCallback(
     const FontFamilyName& aFamily, bool aGeneric, void* aData) {
   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
 
   // for comparisons, force use of unquoted names
   FontFamilyName unquotedFamilyName(aFamily);
-  if (unquotedFamilyName.mType == eFamily_named_quoted) {
-    unquotedFamilyName.mType = eFamily_named;
+  if (unquotedFamilyName.mSyntax == StyleFontFamilyNameSyntax::Quoted) {
+    unquotedFamilyName.mSyntax = StyleFontFamilyNameSyntax::Identifiers;
   }
 
   // Check font family if it is not a generic one
   // We test with the kNullGlyph
   ComputedStyle* sc = context->mChar->mComputedStyle;
   nsFont font = sc->StyleFont()->mFont;
   NormalizeDefaultFont(font, context->mFontSizeInflation);
   RefPtr<gfxFontGroup> fontGroup;
-  FontFamilyList family(unquotedFamilyName);
+  FontFamilyList family(nsTArray<FontFamilyName>{unquotedFamilyName});
   if (!aGeneric &&
       !context->mChar->SetFontFamily(context->mPresContext, nullptr, kNullGlyph,
                                      family, font, &fontGroup))
     return true;  // Could not set the family
 
   // Determine the glyph table to use for this font.
   nsAutoPtr<nsOpenTypeTable> openTypeTable;
   nsGlyphTable* glyphTable;
@@ -1338,17 +1340,18 @@ bool nsMathMLChar::StretchEnumContext::E
     return false;  // no need to continue
 
   return true;  // true means continue
 }
 
 static void AppendFallbacks(nsTArray<FontFamilyName>& aNames,
                             const nsTArray<nsCString>& aFallbacks) {
   for (const nsCString& fallback : aFallbacks) {
-    aNames.AppendElement(FontFamilyName(fallback, eUnquotedName));
+    aNames.AppendElement(
+        FontFamilyName(fallback, StyleFontFamilyNameSyntax::Identifiers));
   }
 }
 
 // insert math fallback families just before the first generic or at the end
 // when no generic present
 static void InsertMathFallbacks(FontFamilyList& aFamilyList,
                                 nsTArray<nsCString>& aFallbacks) {
   nsTArray<FontFamilyName> mergedList;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -86,19 +86,19 @@ using namespace mozilla::css;
 using namespace mozilla::dom;
 
 // Definitions of the global traversal stats.
 bool ServoTraversalStatistics::sActive = false;
 ServoTraversalStatistics ServoTraversalStatistics::sSingleton;
 
 static RWLock* sServoFFILock = nullptr;
 
-static const nsFont* ThreadSafeGetDefaultFontHelper(const Document& aDocument,
-                                                    nsAtom* aLanguage,
-                                                    uint8_t aGenericId) {
+static const nsFont* ThreadSafeGetDefaultFontHelper(
+    const Document& aDocument, nsAtom* aLanguage,
+    StyleGenericFontFamily aGenericId) {
   bool needsCache = false;
   const nsFont* retval;
 
   auto GetDefaultFont = [&](bool* aNeedsToCache) {
     auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, aNeedsToCache);
     return prefs ? prefs->GetDefaultFont(aGenericId) : nullptr;
   };
 
@@ -957,24 +957,24 @@ nsAtom* Gecko_Atomize(const char* aStrin
 nsAtom* Gecko_Atomize16(const nsAString* aString) {
   return NS_Atomize(*aString).take();
 }
 
 void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); }
 
 void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); }
 
-void Gecko_nsTArray_FontFamilyName_AppendNamed(nsTArray<FontFamilyName>* aNames,
-                                               nsAtom* aName, bool aQuoted) {
-  aNames->AppendElement(
-      FontFamilyName(aName, aQuoted ? eQuotedName : eUnquotedName));
+void Gecko_nsTArray_FontFamilyName_AppendNamed(
+    nsTArray<FontFamilyName>* aNames, nsAtom* aName,
+    StyleFontFamilyNameSyntax aSyntax) {
+  aNames->AppendElement(FontFamilyName(aName, aSyntax));
 }
 
 void Gecko_nsTArray_FontFamilyName_AppendGeneric(
-    nsTArray<FontFamilyName>* aNames, FontFamilyType aType) {
+    nsTArray<FontFamilyName>* aNames, StyleGenericFontFamily aType) {
   aNames->AppendElement(FontFamilyName(aType));
 }
 
 SharedFontList* Gecko_SharedFontList_Create() {
   RefPtr<SharedFontList> fontlist = new SharedFontList();
   return fontlist.forget().take();
 }
 
@@ -997,35 +997,35 @@ NS_IMPL_THREADSAFE_FFI_REFCOUNTING(Share
 void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src) {
   dst->fontlist = src->fontlist;
 }
 
 void Gecko_nsFont_InitSystem(nsFont* aDest, int32_t aFontId,
                              const nsStyleFont* aFont,
                              const Document* aDocument) {
   const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper(
-      *aDocument, aFont->mLanguage, kPresContext_DefaultVariableFont_ID);
+      *aDocument, aFont->mLanguage, StyleGenericFontFamily::None);
 
   // We have passed uninitialized memory to this function,
   // initialize it. We can't simply return an nsFont because then
   // we need to know its size beforehand. Servo cannot initialize nsFont
   // itself, so this will do.
   new (aDest) nsFont(*defaultVariableFont);
 
   LookAndFeel::FontID fontID = static_cast<LookAndFeel::FontID>(aFontId);
 
   AutoWriteLock guard(*sServoFFILock);
   nsLayoutUtils::ComputeSystemFont(aDest, fontID, defaultVariableFont);
 }
 
 void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); }
 
-FontFamilyType Gecko_nsStyleFont_ComputeDefaultFontType(const Document* aDoc,
-                                                        uint8_t aGenericId,
-                                                        nsAtom* aLanguage) {
+StyleGenericFontFamily Gecko_nsStyleFont_ComputeDefaultFontType(
+    const Document* aDoc, StyleGenericFontFamily aGenericId,
+    nsAtom* aLanguage) {
   const nsFont* defaultFont =
       ThreadSafeGetDefaultFontHelper(*aDoc, aLanguage, aGenericId);
   return defaultFont->fontlist.GetDefaultFontType();
 }
 
 gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() {
   return new gfxFontFeatureValueSet();
 }
@@ -1887,42 +1887,25 @@ void Gecko_nsStyleFont_SetLang(nsStyleFo
   aFont->mExplicitLanguage = true;
 }
 
 void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont,
                                     const nsStyleFont* aSource) {
   aFont->mLanguage = aSource->mLanguage;
 }
 
-void Gecko_nsStyleFont_PrioritizeUserFonts(nsStyleFont* aFont,
-                                           FontFamilyType aDefaultGeneric) {
+void Gecko_nsStyleFont_PrioritizeUserFonts(
+    nsStyleFont* aFont, StyleGenericFontFamily aDefaultGeneric) {
   MOZ_ASSERT(!StaticPrefs::browser_display_use_document_fonts());
-  MOZ_ASSERT(aDefaultGeneric != eFamily_none);
+  MOZ_ASSERT(aDefaultGeneric != StyleGenericFontFamily::None);
   if (!aFont->mFont.fontlist.PrioritizeFirstGeneric()) {
     aFont->mFont.fontlist.PrependGeneric(aDefaultGeneric);
   }
 }
 
-void Gecko_nsStyleFont_PrefillDefaultForGeneric(nsStyleFont* aFont,
-                                                const Document* aDocument,
-                                                uint8_t aGenericId) {
-  const nsFont* defaultFont =
-      ThreadSafeGetDefaultFontHelper(*aDocument, aFont->mLanguage, aGenericId);
-  // In case of just the language changing, the parent could have had no
-  // generic, which Gecko just does regular cascading with. Do the same. This
-  // can only happen in the case where the language changed but the family did
-  // not
-  if (aGenericId != kGenericFont_NONE) {
-    aFont->mFont.fontlist = defaultFont->fontlist;
-  } else {
-    aFont->mFont.fontlist.SetDefaultFontType(
-        defaultFont->fontlist.GetDefaultFontType());
-  }
-}
-
 nscoord Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont,
                                          const Document* aDocument) {
   // Don't change font-size:0, since that would un-hide hidden text, nor chrome
   // docs, we assume those know what they do.
   if (aFont->mSize == 0 || nsContentUtils::IsChromeDoc(aDocument)) {
     return 0;
   }
 
@@ -1949,17 +1932,16 @@ nscoord Gecko_nsStyleFont_ComputeMinSize
     return 0;
   }
 
   return (minFontSize * aFont->mMinFontSizeRatio) / 100;
 }
 
 void FontSizePrefs::CopyFrom(const LangGroupFontPrefs& prefs) {
   mDefaultVariableSize = prefs.mDefaultVariableFont.size;
-  mDefaultFixedSize = prefs.mDefaultFixedFont.size;
   mDefaultSerifSize = prefs.mDefaultSerifFont.size;
   mDefaultSansSerifSize = prefs.mDefaultSansSerifFont.size;
   mDefaultMonospaceSize = prefs.mDefaultMonospaceFont.size;
   mDefaultCursiveSize = prefs.mDefaultCursiveFont.size;
   mDefaultFantasySize = prefs.mDefaultFantasyFont.size;
 }
 
 FontSizePrefs Gecko_GetBaseSize(nsAtom* aLanguage) {
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -261,20 +261,21 @@ nsAtom* Gecko_Atomize(const char* aStrin
 nsAtom* Gecko_Atomize16(const nsAString* aString);
 void Gecko_AddRefAtom(nsAtom* aAtom);
 void Gecko_ReleaseAtom(nsAtom* aAtom);
 
 // Font style
 void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src);
 
 void Gecko_nsTArray_FontFamilyName_AppendNamed(
-    nsTArray<mozilla::FontFamilyName>* aNames, nsAtom* aName, bool aQuoted);
+    nsTArray<mozilla::FontFamilyName>* aNames, nsAtom* aName,
+    mozilla::StyleFontFamilyNameSyntax);
 
 void Gecko_nsTArray_FontFamilyName_AppendGeneric(
-    nsTArray<mozilla::FontFamilyName>* aNames, mozilla::FontFamilyType aType);
+    nsTArray<mozilla::FontFamilyName>* aNames, mozilla::StyleGenericFontFamily);
 
 // Returns an already-AddRefed SharedFontList with an empty mNames array.
 mozilla::SharedFontList* Gecko_SharedFontList_Create();
 
 size_t Gecko_SharedFontList_SizeOfIncludingThis(
     mozilla::SharedFontList* fontlist);
 
 size_t Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(
@@ -673,28 +674,25 @@ void Gecko_nsStyleFont_CopyLangFrom(nsSt
                                     const nsStyleFont* aSource);
 
 // Moves the generic family in the font-family to the front, or prepends
 // aDefaultGeneric, so that user-configured fonts take precedent over document
 // fonts.
 //
 // Document fonts may still be used as fallback for unsupported glyphs though.
 void Gecko_nsStyleFont_PrioritizeUserFonts(
-    nsStyleFont* font, mozilla::FontFamilyType aDefaultGeneric);
-
-void Gecko_nsStyleFont_PrefillDefaultForGeneric(nsStyleFont* font,
-                                                const mozilla::dom::Document*,
-                                                uint8_t generic_id);
+    nsStyleFont* font, mozilla::StyleGenericFontFamily aDefaultGeneric);
 
 nscoord Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont*,
                                          const mozilla::dom::Document*);
 
 // Computes the default generic font for a generic family and language.
-mozilla::FontFamilyType Gecko_nsStyleFont_ComputeDefaultFontType(
-    const mozilla::dom::Document*, uint8_t generic_family, nsAtom* language);
+mozilla::StyleGenericFontFamily Gecko_nsStyleFont_ComputeDefaultFontType(
+    const mozilla::dom::Document*,
+    mozilla::StyleGenericFontFamily generic_family, nsAtom* language);
 
 mozilla::FontSizePrefs Gecko_GetBaseSize(nsAtom* lang);
 
 // XBL related functions.
 const mozilla::dom::Element* Gecko_GetBindingParent(
     const mozilla::dom::Element*);
 
 const RawServoAuthorStyles* Gecko_XBLBinding_GetRawServoStyles(
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -59,17 +59,16 @@ bitfield-enums = [
 rusty-enums = [
     "nsCompatibility",
     "mozilla::EffectCompositor_CascadeLevel",
     "mozilla::SheetType",
     "mozilla::dom::CallerType",
     "mozilla::dom::IterationCompositeOperation",
     "mozilla::dom::CompositeOperation",
     "mozilla::InheritTarget",
-    "mozilla::FontFamilyType",
     "mozilla::css::DocumentMatchingFunction",
     "mozilla::css::SheetParsingMode",
     "mozilla::StyleContentType",
     "nsStyleSVGOpacitySource",
     "nsStyleUnit",
     "nsCSSKeyword",
     "mozilla::dom::Document_DocumentTheme",
     "mozilla::dom::Document_Type",
@@ -453,16 +452,18 @@ cbindgen-types = [
     { gecko = "StyleRestyleHint", servo = "invalidation::element::restyle_hints::RestyleHint" },
     { gecko = "StyleTouchAction", servo = "values::computed::TouchAction" },
     { gecko = "StyleWillChangeBits", servo = "values::specified::box_::WillChangeBits" },
     { gecko = "StyleTextDecorationLine", servo = "values::computed::TextDecorationLine" },
     { gecko = "StyleMozListReversed", servo = "values::computed::MozListReversed" },
     { gecko = "StyleOwned", servo = "gecko_bindings::sugar::ownership::Owned" },
     { gecko = "StyleOwnedOrNull", servo = "gecko_bindings::sugar::ownership::OwnedOrNull" },
     { gecko = "StyleStrong", servo = "gecko_bindings::sugar::ownership::Strong" },
+    { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
+    { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },