Merge autoland to mozilla-central. a=merge
authorBogdan Szekely <bszekely@mozilla.com>
Tue, 09 Aug 2022 12:31:56 +0300
changeset 626500 aeac2e82d88d2535598de558b8cc9069780f034d
parent 626480 5761a15f36078c1ff69499321706dfaad8da84c3 (current diff)
parent 626499 742754e39bbdb14535be0f23427321abbbeab5b9 (diff)
child 626501 989418af85f753200a5b8e0fe023c783902302f2
child 626509 3ae3d7948f18fe758cc02a5c232742d37cb173f6
push id40103
push userbszekely@mozilla.com
push dateTue, 09 Aug 2022 09:33:38 +0000
treeherdermozilla-central@aeac2e82d88d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone105.0a1
first release with
nightly linux32
aeac2e82d88d / 105.0a1 / 20220809093338 / files
nightly linux64
aeac2e82d88d / 105.0a1 / 20220809093338 / files
nightly mac
aeac2e82d88d / 105.0a1 / 20220809093338 / files
nightly win32
aeac2e82d88d / 105.0a1 / 20220809093338 / files
nightly win64
aeac2e82d88d / 105.0a1 / 20220809093338 / 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
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1943,17 +1943,17 @@ pref("browser.contentblocking.report.vpn
 // Avoid advertising Focus in certain regions.  Comma separated string of two letter
 // ISO 3166-1 country codes.
 pref("browser.promo.focus.disallowed_regions", "cn");
 
 // Default to enabling focus promos to be shown where allowed.
 pref("browser.promo.focus.enabled", true);
 
 // Default to enabling pin promos to be shown where allowed.
-pref("browser.promo.pin.enabled", true);
+pref("browser.promo.pin.enabled", false);
 
 // Comma separated string of mozilla vpn supported platforms.
 pref("browser.contentblocking.report.vpn_platforms", "win,mac,linux");
 pref("browser.contentblocking.report.hide_vpn_banner", false);
 pref("browser.contentblocking.report.vpn_sub_id", "sub_HrfCZF7VPHzZkA");
 
 pref("browser.contentblocking.report.monitor.url", "https://monitor.firefox.com/?entrypoint=protection_report_monitor&utm_source=about-protections");
 pref("browser.contentblocking.report.monitor.how_it_works.url", "https://monitor.firefox.com/about");
--- a/browser/components/newtab/lib/OnboardingMessageProvider.jsm
+++ b/browser/components/newtab/lib/OnboardingMessageProvider.jsm
@@ -445,16 +445,86 @@ const ONBOARDING_MESSAGES = () => [
       infoIcon: "",
       infoTitle: "",
       infoBody: "fluent:about-private-browsing-info-description-private-window",
       infoLinkText: "fluent:about-private-browsing-learn-more-link",
       infoTitleEnabled: false,
     },
     targeting: "true",
   },
+  {
+    id: "PB_NEWTAB_PIN_PROMO",
+    template: "pb_newtab",
+    type: "default",
+    groups: ["pbNewtab"],
+    content: {
+      infoBody: "fluent:about-private-browsing-info-description-simplified",
+      infoEnabled: true,
+      infoIcon: "chrome://global/skin/icons/indicator-private-browsing.svg",
+      infoLinkText: "fluent:about-private-browsing-learn-more-link",
+      infoTitle: "",
+      infoTitleEnabled: false,
+      promoEnabled: true,
+      promoType: "PIN",
+      promoHeader: "fluent:about-private-browsing-pin-promo-header",
+      promoImageLarge:
+        "chrome://browser/content/assets/private-promo-asset.svg",
+      promoLinkText: "fluent:about-private-browsing-pin-promo-link-text",
+      promoLinkType: "button",
+      promoSectionStyle: "below-search",
+      promoTitle: "fluent:about-private-browsing-pin-promo-title",
+      promoTitleEnabled: true,
+      promoButton: {
+        action: {
+          type: "MULTI_ACTION",
+          data: {
+            actions: [
+              {
+                type: "SET_PREF",
+                data: {
+                  pref: {
+                    name:
+                      "browser.privacySegmentation.windowSeparation.enabled",
+                    value: true,
+                  },
+                },
+              },
+              {
+                type: "PIN_FIREFOX_TO_TASKBAR",
+                data: {
+                  privatePin: true,
+                },
+              },
+              {
+                type: "BLOCK_MESSAGE",
+                data: {
+                  id: "PB_NEWTAB_PIN_PROMO",
+                },
+              },
+              {
+                type: "OPEN_ABOUT_PAGE",
+                data: { args: "privatebrowsing", where: "current" },
+              },
+            ],
+          },
+        },
+      },
+    },
+    priority: 3,
+    frequency: {
+      custom: [
+        {
+          cap: 3,
+          period: 604800000, // Max 3 per week
+        },
+      ],
+      lifetime: 12,
+    },
+    targeting: "doesAppNeedPrivatePin",
+  },
 ];
 
 const OnboardingMessageProvider = {
   async getExtraAttributes() {
     const [header, button_label] = await L10N.formatMessages([
       { id: "onboarding-welcome-header" },
       { id: "onboarding-start-browsing-button-label" },
     ]);
--- a/browser/components/newtab/lib/PanelTestProvider.jsm
+++ b/browser/components/newtab/lib/PanelTestProvider.jsm
@@ -657,17 +657,17 @@ const MESSAGES = () => [
         },
       },
     },
     groups: ["panel-test-provider"],
     targeting: "region != 'CN' && !hasActiveEnterprisePolicies",
     frequency: { lifetime: 3 },
   },
   {
-    id: "PB_NEWTAB_PIN_PROMO",
+    id: "PB_PIN_PROMO",
     template: "pb_newtab",
     groups: ["pbNewtab"],
     content: {
       infoBody: "fluent:about-private-browsing-info-description-simplified",
       infoEnabled: true,
       infoIcon: "chrome://global/skin/icons/indicator-private-browsing.svg",
       infoLinkText: "fluent:about-private-browsing-learn-more-link",
       infoTitle: "",
@@ -699,17 +699,17 @@ const MESSAGES = () => [
                 },
               },
               {
                 type: "PIN_FIREFOX_TO_TASKBAR",
               },
               {
                 type: "BLOCK_MESSAGE",
                 data: {
-                  id: "PB_NEWTAB_PIN_PROMO",
+                  id: "PB_PIN_PROMO",
                 },
               },
               {
                 type: "OPEN_ABOUT_PAGE",
                 data: { args: "privatebrowsing", where: "current" },
               },
             ],
           },
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -16,16 +16,17 @@ support-files =
   empty_file.html
   file_favicon.html
   file_favicon.png
   file_favicon.png^headers^
   file_triggeringprincipal_oa.html
 
 [browser_privatebrowsing_DownloadLastDirWithCPS.js]
 [browser_privatebrowsing_about_default_promo.js]
+[browser_privatebrowsing_about_default_pin_promo.js]
 [browser_privatebrowsing_about_focus_promo.js]
 [browser_privatebrowsing_about_nimbus.js]
 [browser_privatebrowsing_about_nimbus_messaging.js]
 [browser_privatebrowsing_about_nimbus_impressions.js]
 [browser_privatebrowsing_about_nimbus_dismiss.js]
 [browser_privatebrowsing_about.js]
 skip-if =
   verify
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_default_pin_promo.js
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from head.js */
+const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+add_setup(async function() {
+  ASRouter.resetMessageState();
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.promo.pin.enabled", true]],
+  });
+  await ASRouter.onPrefChange();
+});
+
+add_task(async function test_pin_promo() {
+  const sandbox = sinon.createSandbox();
+  // Stub out the doesAppNeedPin to true so that Pin Promo targeting evaluates true
+  const { ShellService } = ChromeUtils.import(
+    "resource:///modules/ShellService.jsm"
+  );
+  sandbox
+    .stub(ShellService, "doesAppNeedPin")
+    .withArgs(true)
+    .returns(true);
+  registerCleanupFunction(async () => {
+    ASRouter.resetMessageState();
+    sandbox.restore();
+  });
+  let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
+
+  await SpecialPowers.spawn(tab1, [], async function() {
+    const promoContainer = content.document.querySelector(".promo");
+    const promoHeader = content.document.getElementById("promo-header");
+
+    ok(promoContainer, "Pin promo is shown");
+    is(
+      promoHeader.textContent,
+      "Private browsing freedom in one click",
+      "Correct default values are shown"
+    );
+  });
+
+  let { win: win2 } = await openTabAndWaitForRender();
+  let { win: win3 } = await openTabAndWaitForRender();
+  let { win: win4, tab: tab4 } = await openTabAndWaitForRender();
+
+  await SpecialPowers.spawn(tab4, [], async function() {
+    is(
+      content.document.getElementById(".private-browsing-promo-link"),
+      null,
+      "should no longer render the promo after 3 impressions"
+    );
+  });
+
+  await BrowserTestUtils.closeWindow(win1);
+  await BrowserTestUtils.closeWindow(win2);
+  await BrowserTestUtils.closeWindow(win3);
+  await BrowserTestUtils.closeWindow(win4);
+});
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -430,17 +430,17 @@ function formAutoFillCommonSetup() {
   formFillChromeScript = SpecialPowers.loadChromeScript(chromeURL);
   formFillChromeScript.addMessageListener("onpopupshown", ({ results }) => {
     gLastAutoCompleteResults = results;
     if (gPopupShownListener) {
       gPopupShownListener({ results });
     }
   });
 
-  add_task(async function setup() {
+  add_setup(async () => {
     info(`expecting the storage setup`);
     await formFillChromeScript.sendQuery("setup");
   });
 
   SimpleTest.registerCleanupFunction(async () => {
     info(`expecting the storage cleanup`);
     await formFillChromeScript.sendQuery("cleanup");
 
--- a/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
@@ -161,17 +161,17 @@ function checkFormFilled(selector, addre
 async function setupAddressStorage() {
   for (let address of MOCK_STORAGE) {
     await addAddress(address);
   }
 }
 
 initPopupListener();
 
-add_task(async function setup() {
+add_setup(async () => {
   // This test relies on being able to fill a Canadian address which isn't possible
   // without `supportedCountries` allowing Canada
   await SpecialPowers.pushPrefEnv({"set": [["extensions.formautofill.supportedCountries", "US,CA"]]});
 
   await setupAddressStorage();
 });
 
 // Autofill the address with address level 1 code.
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -198,17 +198,17 @@ function verifySectionFieldDetails(secti
     });
   });
 }
 
 var FormAutofillHeuristics, LabelUtils;
 var AddressDataLoader, FormAutofillUtils;
 
 async function runHeuristicsTest(patterns, fixturePathPrefix) {
-  add_task(async function setup() {
+  add_setup(async () => {
     ({ FormAutofillHeuristics } = ChromeUtils.import(
       "resource://autofill/FormAutofillHeuristics.jsm"
     ));
     ({ AddressDataLoader, FormAutofillUtils, LabelUtils } = ChromeUtils.import(
       "resource://autofill/FormAutofillUtils.jsm"
     ));
   });
 
--- a/browser/extensions/formautofill/test/unit/heuristics/test_known_strings.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/test_known_strings.js
@@ -122,17 +122,17 @@ const KNOWN_NAMES = {
     "expiration-date-yy",
     "expiration-date-yyyy",
     "validity-year",
     "exp-date-year",
     "date-y",
   ],
 };
 
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillHeuristics } = ChromeUtils.import(
     "resource://autofill/FormAutofillHeuristics.jsm"
   ));
 });
 
 for (let field in KNOWN_NAMES) {
   KNOWN_NAMES[field].forEach(name => {
     add_task(async () => {
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -1,17 +1,17 @@
 /*
  * Test for status handling in Form Autofill Parent.
  */
 
 "use strict";
 
 let FormAutofillStatus;
 
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStatus } = ChromeUtils.import(
     "resource://autofill/FormAutofillParent.jsm"
   ));
 });
 
 add_task(async function test_activeStatus_init() {
   sinon.spy(FormAutofillStatus, "updateStatus");
 
--- a/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
+++ b/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
@@ -11,17 +11,17 @@ const SUPPORT_COUNTRIES_TESTCASES = [
   },
   {
     country: "DE",
     properties: ["name"],
   },
 ];
 
 var AddressDataLoader, FormAutofillUtils;
-add_task(async function setup() {
+add_setup(async () => {
   ({ AddressDataLoader, FormAutofillUtils } = ChromeUtils.import(
     "resource://autofill/FormAutofillUtils.jsm"
   ));
 });
 
 add_task(async function test_initalState() {
   // addressData should not exist
   Assert.equal(AddressDataLoader._addressData, undefined);
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -5,17 +5,17 @@
 
 "use strict";
 
 const { setTimeout, clearTimeout } = ChromeUtils.import(
   "resource://gre/modules/Timer.jsm"
 );
 
 var FormAutofillHandler, OSKeyStore;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillHandler } = ChromeUtils.import(
     "resource://autofill/FormAutofillHandler.jsm"
   ));
   ({ OSKeyStore } = ChromeUtils.import(
     "resource://gre/modules/OSKeyStore.jsm"
   ));
 });
 
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const { AppConstants } = ChromeUtils.import(
   "resource://gre/modules/AppConstants.jsm"
 );
 
 var FormAutofillHandler;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillHandler } = ChromeUtils.import(
     "resource://autofill/FormAutofillHandler.jsm"
   ));
 });
 
 const TESTCASES = [
   {
     description: "Form without autocomplete property",
--- a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
@@ -9,17 +9,17 @@ ChromeUtils.defineModuleGetter(
   "Preferences",
   "resource://gre/modules/Preferences.jsm"
 );
 const { CreditCard } = ChromeUtils.import(
   "resource://gre/modules/CreditCard.jsm"
 );
 
 let FormAutofillStorage;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStorage } = ChromeUtils.import(
     "resource://autofill/FormAutofillStorage.jsm"
   ));
 });
 
 const TEST_STORE_FILE_NAME = "test-credit-card.json";
 const COLLECTION_NAME = "creditCards";
 
--- a/browser/extensions/formautofill/test/unit/test_getRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_getRecords.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const { CreditCard } = ChromeUtils.import(
   "resource://gre/modules/CreditCard.jsm"
 );
 
 let FormAutofillParent, FormAutofillStatus;
 let OSKeyStore;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillParent, FormAutofillStatus } = ChromeUtils.import(
     "resource://autofill/FormAutofillParent.jsm"
   ));
   ({ OSKeyStore } = ChromeUtils.import(
     "resource://gre/modules/OSKeyStore.jsm"
   ));
 });
 
--- a/browser/extensions/formautofill/test/unit/test_isCJKName.js
+++ b/browser/extensions/formautofill/test/unit/test_isCJKName.js
@@ -1,16 +1,16 @@
 /**
  * Tests the "isCJKName" function of FormAutofillNameUtils object.
  */
 
 "use strict";
 
 var FormAutofillNameUtils;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillNameUtils } = ChromeUtils.import(
     "resource://autofill/FormAutofillNameUtils.jsm"
   ));
 });
 
 // Test cases is initially copied from
 // https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
 const TESTCASES = [
--- a/browser/extensions/formautofill/test/unit/test_isCreditCardOrAddressFieldType.js
+++ b/browser/extensions/formautofill/test/unit/test_isCreditCardOrAddressFieldType.js
@@ -1,12 +1,12 @@
 "use strict";
 
 var FormAutofillUtils;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillUtils } = ChromeUtils.import(
     "resource://autofill/FormAutofillUtils.jsm"
   ));
 });
 
 const TESTCASES = [
   {
     document: `<input id="targetElement" type="text">`,
--- a/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
+++ b/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
@@ -166,17 +166,17 @@ const TESTCASES = [
         ]
       : ["cc-number5", "cc-name", "cc-exp-month", "cc-exp-year"],
   },
 ];
 
 let markedFieldId = [];
 
 var FormAutofillContent;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillContent } = ChromeUtils.import(
     "resource://autofill/FormAutofillContent.jsm"
   ));
 
   FormAutofillContent._markAsAutofillField = function(field) {
     markedFieldId.push(field.id);
   };
 });
--- a/browser/extensions/formautofill/test/unit/test_migrateRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_migrateRecords.js
@@ -1,16 +1,16 @@
 /**
  * Tests the migration algorithm in profileStorage.
  */
 
 "use strict";
 
 let FormAutofillStorage;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStorage } = ChromeUtils.import(
     "resource://autofill/FormAutofillStorage.jsm"
   ));
 });
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const ADDRESS_SCHEMA_VERSION = 1;
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -1,12 +1,12 @@
 "use strict";
 
 var FormAutofillContent;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillContent } = ChromeUtils.import(
     "resource://autofill/FormAutofillContent.jsm"
   ));
 });
 
 const DEFAULT_TEST_DOC = `<form id="form1">
                       <input id="street-addr" autocomplete="street-address">
                       <select id="address-level1" autocomplete="address-level1">
--- a/browser/extensions/formautofill/test/unit/test_parseAddressFormat.js
+++ b/browser/extensions/formautofill/test/unit/test_parseAddressFormat.js
@@ -1,12 +1,12 @@
 "use strict";
 
 var FormAutofillUtils;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillUtils } = ChromeUtils.import(
     "resource://autofill/FormAutofillUtils.jsm"
   ));
 });
 
 add_task(async function test_parseAddressFormat() {
   const TEST_CASES = [
     {
--- a/browser/extensions/formautofill/test/unit/test_phoneNumber.js
+++ b/browser/extensions/formautofill/test/unit/test_phoneNumber.js
@@ -1,16 +1,16 @@
 /**
  * Tests PhoneNumber.jsm and PhoneNumberNormalizer.jsm.
  */
 
 "use strict";
 
 var PhoneNumber, PhoneNumberNormalizer;
-add_task(async function setup() {
+add_setup(async () => {
   ({ PhoneNumber } = ChromeUtils.import(
     "resource://autofill/phonenumberutils/PhoneNumber.jsm"
   ));
   ({ PhoneNumberNormalizer } = ChromeUtils.import(
     "resource://autofill/phonenumberutils/PhoneNumberNormalizer.jsm"
   ));
 });
 
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -1,12 +1,12 @@
 "use strict";
 
 var AddressResult, CreditCardResult;
-add_task(async function setup() {
+add_setup(async () => {
   ({ AddressResult, CreditCardResult } = ChromeUtils.import(
     "resource://autofill/ProfileAutoCompleteResult.jsm"
   ));
 });
 
 let matchingProfiles = [
   {
     guid: "test-guid-1",
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -1,17 +1,17 @@
 /*
  * Test for keeping the valid fields information in sharedData.
  */
 
 "use strict";
 
 let FormAutofillStatus;
 
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStatus } = ChromeUtils.import(
     "resource://autofill/FormAutofillParent.jsm"
   ));
 });
 
 add_task(async function test_profileSavedFieldNames_init() {
   FormAutofillStatus.init();
   sinon.stub(FormAutofillStatus, "updateSavedFieldNames");
--- a/browser/extensions/formautofill/test/unit/test_storage_remove.js
+++ b/browser/extensions/formautofill/test/unit/test_storage_remove.js
@@ -1,16 +1,16 @@
 /**
  * Tests removing all address/creditcard records.
  */
 
 "use strict";
 
 let FormAutofillStorage;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStorage } = ChromeUtils.import(
     "resource://autofill/FormAutofillStorage.jsm"
   ));
 });
 
 const TEST_STORE_FILE_NAME = "test-tombstones.json";
 
 const TEST_ADDRESS_1 = {
--- a/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
+++ b/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
@@ -1,16 +1,16 @@
 /**
  * Tests tombstones in address/creditcard records.
  */
 
 "use strict";
 
 let FormAutofillStorage;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStorage } = ChromeUtils.import(
     "resource://autofill/FormAutofillStorage.jsm"
   ));
 });
 
 const TEST_STORE_FILE_NAME = "test-tombstones.json";
 
 const TEST_ADDRESS_1 = {
--- a/browser/extensions/formautofill/test/unit/test_toOneLineAddress.js
+++ b/browser/extensions/formautofill/test/unit/test_toOneLineAddress.js
@@ -1,12 +1,12 @@
 "use strict";
 
 var FormAutofillUtils;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillUtils } = ChromeUtils.import(
     "resource://autofill/FormAutofillUtils.jsm"
   ));
 });
 
 add_task(async function test_getCategoriesFromFieldNames() {
   const TEST_CASES = [
     {
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -1,16 +1,16 @@
 /**
  * Tests the transform algorithm in profileStorage.
  */
 
 "use strict";
 
 let FormAutofillStorage;
-add_task(async function setup() {
+add_setup(async () => {
   ({ FormAutofillStorage } = ChromeUtils.import(
     "resource://autofill/FormAutofillStorage.jsm"
   ));
 });
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const ADDRESS_COMPUTE_TESTCASES = [
--- a/browser/locales/en-US/browser/aboutPrivateBrowsing.ftl
+++ b/browser/locales/en-US/browser/aboutPrivateBrowsing.ftl
@@ -49,8 +49,17 @@ about-private-browsing-search-banner-des
      [windows] To select a different search engine go to <a data-l10n-name="link-options">Options</a>
     *[other] To select a different search engine go to <a data-l10n-name="link-options">Preferences</a>
   }
 about-private-browsing-search-banner-close-button =
     .aria-label = Close
 
 about-private-browsing-promo-close-button =
   .title = Close
+
+## Strings used in a “pin promotion” message, which prompts users to pin a private window
+
+about-private-browsing-pin-promo-header = Private browsing freedom in one click
+about-private-browsing-pin-promo-link-text = { PLATFORM() ->
+    [macos] Keep in Dock
+   *[other] Pin to taskbar
+}
+about-private-browsing-pin-promo-title = No saved cookies or history, right from your desktop. Browse like no one’s watching.
--- a/editor/libeditor/AutoRangeArray.cpp
+++ b/editor/libeditor/AutoRangeArray.cpp
@@ -81,41 +81,41 @@ AutoRangeArray::~AutoRangeArray() {
 bool AutoRangeArray::IsEditableRange(const dom::AbstractRange& aRange,
                                      const Element& aEditingHost) {
   // TODO: Perhaps, we should check whether the start/end boundaries are
   //       first/last point of non-editable element.
   //       See https://github.com/w3c/editing/issues/283#issuecomment-788654850
   EditorRawDOMPoint atStart(aRange.StartRef());
   const bool isStartEditable =
       atStart.IsInContentNode() &&
-      EditorUtils::IsEditableContent(*atStart.ContainerAsContent(),
+      EditorUtils::IsEditableContent(*atStart.ContainerAs<nsIContent>(),
                                      EditorUtils::EditorType::HTML) &&
       !HTMLEditUtils::IsNonEditableReplacedContent(
-          *atStart.ContainerAsContent());
+          *atStart.ContainerAs<nsIContent>());
   if (!isStartEditable) {
     return false;
   }
 
   if (aRange.GetStartContainer() != aRange.GetEndContainer()) {
     EditorRawDOMPoint atEnd(aRange.EndRef());
     const bool isEndEditable =
         atEnd.IsInContentNode() &&
-        EditorUtils::IsEditableContent(*atEnd.ContainerAsContent(),
+        EditorUtils::IsEditableContent(*atEnd.ContainerAs<nsIContent>(),
                                        EditorUtils::EditorType::HTML) &&
         !HTMLEditUtils::IsNonEditableReplacedContent(
-            *atEnd.ContainerAsContent());
+            *atEnd.ContainerAs<nsIContent>());
     if (!isEndEditable) {
       return false;
     }
 
     // Now, both start and end points are editable, but if they are in
     // different editing host, we cannot edit the range.
-    if (atStart.ContainerAsContent() != atEnd.ContainerAsContent() &&
-        atStart.ContainerAsContent()->GetEditingHost() !=
-            atEnd.ContainerAsContent()->GetEditingHost()) {
+    if (atStart.ContainerAs<nsIContent>() != atEnd.ContainerAs<nsIContent>() &&
+        atStart.ContainerAs<nsIContent>()->GetEditingHost() !=
+            atEnd.ContainerAs<nsIContent>()->GetEditingHost()) {
       return false;
     }
   }
 
   // HTMLEditor does not support modifying outside `<body>` element for now.
   nsINode* commonAncestor = aRange.GetClosestCommonInclusiveAncestor();
   return commonAncestor && commonAncestor->IsContent() &&
          commonAncestor->IsInclusiveDescendantOf(&aEditingHost);
@@ -290,17 +290,17 @@ AutoRangeArray::ExtendAnchorFocusRangeFo
         return aDirectionAndAmount;
       }
 
       if (!insertionPoint.IsInTextNode()) {
         return aDirectionAndAmount;
       }
 
       const nsTextFragment* data =
-          &insertionPoint.GetContainerAsText()->TextFragment();
+          &insertionPoint.ContainerAs<Text>()->TextFragment();
       uint32_t offset = insertionPoint.Offset();
       if (!(offset > 1 &&
             data->IsLowSurrogateFollowingHighSurrogateAt(offset - 1)) &&
           !(offset > 0 &&
             gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) {
         return aDirectionAndAmount;
       }
       // Different from the `eNext` case, we look for character boundary.
@@ -480,17 +480,17 @@ void AutoRangeArray::
   //     and may not select in non-editable block.  However, for inline
   //     editing host case, it's right to look for block element without
   //     editable state check.  Now, this method is used for preparation for
   //     other things.  So, cannot write test for this method behavior.
   //     So, perhaps, we should get rid of this method and each caller should
   //     handle its job better.
   Element* const maybeNonEditableBlockElement =
       HTMLEditUtils::GetInclusiveAncestorElement(
-          *aStartPoint.ContainerAsContent(),
+          *aStartPoint.ContainerAs<nsIContent>(),
           HTMLEditUtils::ClosestBlockElement);
   if (!maybeNonEditableBlockElement) {
     return;
   }
 
   // Make sure we don't go higher than our root element in the content tree
   if (aEditingHost.IsInclusiveDescendantOf(maybeNonEditableBlockElement)) {
     return;
@@ -657,17 +657,17 @@ static EditorDOMPoint GetPointAfterFollo
         if (maybeNonEditableBlockElement == &aEditingHost ||
             !maybeNonEditableBlockElement->IsInclusiveDescendantOf(
                 &aEditingHost)) {
           return EditorDOMPoint::AtEndOf(*maybeNonEditableBlockElement);
         }
         // If it's invisible because of parent block boundary, return end
         // of the block.  Otherwise, i.e., it's followed by a child block,
         // returns the point of the child block.
-        if (atNextPreformattedNewLine.ContainerAsText()
+        if (atNextPreformattedNewLine.ContainerAs<Text>()
                 ->IsInclusiveDescendantOf(maybeNonEditableBlockElement)) {
           return EditorDOMPoint::AtEndOf(*maybeNonEditableBlockElement);
         }
         return EditorDOMPoint(maybeNonEditableBlockElement);
       }
       // Otherwise, return the point after the preformatted linefeed.
       return atNextPreformattedNewLine.NextPoint();
     }
@@ -717,17 +717,17 @@ static EditorDOMPoint GetPointAfterFollo
         if (maybeNonEditableBlockElement == &aEditingHost ||
             !maybeNonEditableBlockElement->IsInclusiveDescendantOf(
                 &aEditingHost)) {
           return EditorDOMPoint::AtEndOf(*maybeNonEditableBlockElement);
         }
         // If it's invisible because of parent block boundary, return end
         // of the block.  Otherwise, i.e., it's followed by a child block,
         // returns the point of the child block.
-        if (atFirstPreformattedNewLine.ContainerAsText()
+        if (atFirstPreformattedNewLine.ContainerAs<Text>()
                 ->IsInclusiveDescendantOf(maybeNonEditableBlockElement)) {
           return EditorDOMPoint::AtEndOf(*maybeNonEditableBlockElement);
         }
         return EditorDOMPoint(maybeNonEditableBlockElement);
       }
       // Otherwise, return the point after the preformatted linefeed.
       return atFirstPreformattedNewLine.NextPoint();
     }
--- a/editor/libeditor/CompositionTransaction.cpp
+++ b/editor/libeditor/CompositionTransaction.cpp
@@ -33,34 +33,34 @@ already_AddRefed<CompositionTransaction>
   MOZ_RELEASE_ASSERT(composition);
   // XXX Actually, we get different text node and offset from editor in some
   //     cases.  If composition stores text node, we should use it and offset
   //     in it.
   EditorDOMPointInText pointToInsert;
   if (Text* textNode = composition->GetContainerTextNode()) {
     pointToInsert.Set(textNode, composition->XPOffsetInTextNode());
     NS_WARNING_ASSERTION(
-        pointToInsert.GetContainerAsText() ==
+        pointToInsert.GetContainerAs<Text>() ==
             composition->GetContainerTextNode(),
         "The editor tries to insert composition string into different node");
     NS_WARNING_ASSERTION(
         pointToInsert.Offset() == composition->XPOffsetInTextNode(),
         "The editor tries to insert composition string into different offset");
   } else {
     pointToInsert = aPointToInsert;
   }
   RefPtr<CompositionTransaction> transaction =
       new CompositionTransaction(aEditorBase, aStringToInsert, pointToInsert);
   return transaction.forget();
 }
 
 CompositionTransaction::CompositionTransaction(
     EditorBase& aEditorBase, const nsAString& aStringToInsert,
     const EditorDOMPointInText& aPointToInsert)
-    : mTextNode(aPointToInsert.ContainerAsText()),
+    : mTextNode(aPointToInsert.ContainerAs<Text>()),
       mOffset(aPointToInsert.Offset()),
       mReplaceLength(aEditorBase.GetComposition()->XPLengthInTextNode()),
       mRanges(aEditorBase.GetComposition()->GetRanges()),
       mStringToInsert(aStringToInsert),
       mEditorBase(&aEditorBase),
       mFixed(false) {
   MOZ_ASSERT(mTextNode->TextLength() >= mOffset);
 }
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2796,27 +2796,27 @@ EditorDOMPointType EditorBase::FindBette
       }
     }
   }
 
   // Sometimes, aNode is the padding <br> element itself.  In that case, we'll
   // adjust the insertion point to the previous text node, if one exists, or
   // to the parent anonymous DIV.
   if (EditorUtils::IsPaddingBRElementForEmptyLastLine(
-          *aPoint.ContainerAsContent()) &&
+          *aPoint.template ContainerAs<nsIContent>()) &&
       aPoint.IsStartOfContainer()) {
     nsIContent* previousSibling = aPoint.GetContainer()->GetPreviousSibling();
     if (previousSibling && previousSibling->IsText()) {
       return EditorDOMPointType::AtEndOf(*previousSibling);
     }
 
     nsINode* parentOfContainer = aPoint.GetContainerParent();
     if (parentOfContainer && parentOfContainer == rootElement) {
-      return EditorDOMPointType(parentOfContainer, aPoint.ContainerAsContent(),
-                                0u);
+      return EditorDOMPointType(parentOfContainer,
+                                aPoint.template ContainerAs<nsIContent>(), 0u);
     }
   }
 
   return aPoint;
 }
 
 Result<EditorDOMPoint, nsresult> EditorBase::InsertTextWithTransaction(
     Document& aDocument, const nsAString& aStringToInsert,
@@ -2907,17 +2907,17 @@ Result<EditorDOMPoint, nsresult> EditorB
   if (pointToInsert.IsInTextNode()) {
     CheckedUint32 newOffset = aStringToInsert.Length();
     newOffset += pointToInsert.Offset();
     if (NS_WARN_IF(!newOffset.isValid())) {
       return Err(NS_ERROR_FAILURE);
     }
     // we are inserting text into an existing text node.
     nsresult rv = InsertTextIntoTextNodeWithTransaction(
-        aStringToInsert, EditorDOMPointInText(pointToInsert.ContainerAsText(),
+        aStringToInsert, EditorDOMPointInText(pointToInsert.ContainerAs<Text>(),
                                               pointToInsert.Offset()));
     if (MOZ_UNLIKELY(Destroyed())) {
       NS_WARNING(
           "EditorBase::InsertTextIntoTextNodeWithTransaction() caused "
           "destroying the editor");
       return Err(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_FAILED(rv)) {
@@ -2970,44 +2970,44 @@ static bool TextFragmentBeginsWithString
 
   return aString.EqualsLatin1(aTextFragment.Get1b() + aOffset, stringLength);
 }
 
 static std::tuple<EditorDOMPointInText, EditorDOMPointInText>
 AdjustTextInsertionRange(const EditorDOMPointInText& aInsertedPoint,
                          const nsAString& aInsertedString) {
   if (TextFragmentBeginsWithStringAtOffset(
-          aInsertedPoint.ContainerAsText()->TextFragment(),
+          aInsertedPoint.ContainerAs<Text>()->TextFragment(),
           aInsertedPoint.Offset(), aInsertedString)) {
     return {aInsertedPoint,
             EditorDOMPointInText(
-                aInsertedPoint.ContainerAsText(),
+                aInsertedPoint.ContainerAs<Text>(),
                 aInsertedPoint.Offset() + aInsertedString.Length())};
   }
 
-  return {EditorDOMPointInText(aInsertedPoint.ContainerAsText(), 0),
-          EditorDOMPointInText::AtEndOf(*aInsertedPoint.ContainerAsText())};
+  return {EditorDOMPointInText(aInsertedPoint.ContainerAs<Text>(), 0),
+          EditorDOMPointInText::AtEndOf(*aInsertedPoint.ContainerAs<Text>())};
 }
 
 std::tuple<EditorDOMPointInText, EditorDOMPointInText>
 EditorBase::ComputeInsertedRange(const EditorDOMPointInText& aInsertedPoint,
                                  const nsAString& aInsertedString) const {
   MOZ_ASSERT(aInsertedPoint.IsSet());
 
   // The DOM was potentially modified during the transaction. This is possible
   // through mutation event listeners. That is, the node could've been removed
   // from the doc or otherwise modified.
   if (!MayHaveMutationEventListeners(
           NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED)) {
     EditorDOMPointInText endOfInsertion(
-        aInsertedPoint.ContainerAsText(),
+        aInsertedPoint.ContainerAs<Text>(),
         aInsertedPoint.Offset() + aInsertedString.Length());
     return {aInsertedPoint, endOfInsertion};
   }
-  if (aInsertedPoint.ContainerAsText()->IsInComposedDoc()) {
+  if (aInsertedPoint.ContainerAs<Text>()->IsInComposedDoc()) {
     EditorDOMPointInText begin, end;
     return AdjustTextInsertionRange(aInsertedPoint, aInsertedString);
   }
   return {EditorDOMPointInText(), EditorDOMPointInText()};
 }
 
 nsresult EditorBase::InsertTextIntoTextNodeWithTransaction(
     const nsAString& aStringToInsert,
@@ -3049,27 +3049,27 @@ nsresult EditorBase::InsertTextIntoTextN
     auto [begin, end] = ComputeInsertedRange(pointToInsert, aStringToInsert);
     if (begin.IsSet() && end.IsSet()) {
       TopLevelEditSubActionDataRef().DidInsertText(
           *this, begin.To<EditorRawDOMPoint>(), end.To<EditorRawDOMPoint>());
     }
     if (isIMETransaction) {
       // Let's mark the text node as "modified frequently" if it interact with
       // IME since non-ASCII character may be inserted into it in most cases.
-      aPointToInsert.ContainerAsText()->MarkAsMaybeModifiedFrequently();
+      aPointToInsert.ContainerAs<Text>()->MarkAsMaybeModifiedFrequently();
     }
   }
 
   // let listeners know what happened
   if (!mActionListeners.IsEmpty()) {
     for (auto& listener : mActionListeners.Clone()) {
       // TODO: might need adaptation because of mutation event listeners called
       // during `DoTransactionInternal`.
       DebugOnly<nsresult> rvIgnored =
-          listener->DidInsertText(pointToInsert.ContainerAsText(),
+          listener->DidInsertText(pointToInsert.ContainerAs<Text>(),
                                   pointToInsert.Offset(), aStringToInsert, rv);
       NS_WARNING_ASSERTION(
           NS_SUCCEEDED(rvIgnored),
           "nsIEditActionListener::DidInsertText() failed, but ignored");
     }
   }
 
   // Added some cruft here for bug 43366.  Layout was crashing because we left
@@ -3890,19 +3890,19 @@ EditorBase::CreateTransactionForCollapse
         } else {
           point.SetToEndOf(anonymousDiv->GetFirstChild());
         }
       } else {
         // Must be referring a padding `<br>` element or after the text node.
         point.SetToEndOf(anonymousDiv->GetFirstChild());
       }
     }
-    MOZ_ASSERT(!point.ContainerAsText()->GetPreviousSibling());
-    MOZ_ASSERT(!point.ContainerAsText()->GetNextSibling() ||
-               !point.ContainerAsText()->GetNextSibling()->IsText());
+    MOZ_ASSERT(!point.ContainerAs<Text>()->GetPreviousSibling());
+    MOZ_ASSERT(!point.ContainerAs<Text>()->GetNextSibling() ||
+               !point.ContainerAs<Text>()->GetNextSibling()->IsText());
     if (aHowToHandleCollapsedRange ==
             HowToHandleCollapsedRange::ExtendBackward &&
         point.IsStartOfContainer()) {
       return nullptr;
     }
     if (aHowToHandleCollapsedRange ==
             HowToHandleCollapsedRange::ExtendForward &&
         point.IsEndOfContainer()) {
@@ -4013,25 +4013,25 @@ EditorBase::CreateTransactionForCollapse
     return deleteNodeTransaction.forget();
   }
 
   if (point.IsInTextNode()) {
     if (aHowToHandleCollapsedRange ==
         HowToHandleCollapsedRange::ExtendBackward) {
       RefPtr<DeleteTextTransaction> deleteTextTransaction =
           DeleteTextTransaction::MaybeCreateForPreviousCharacter(
-              *this, *point.ContainerAsText(), point.Offset());
+              *this, *point.ContainerAs<Text>(), point.Offset());
       NS_WARNING_ASSERTION(
           deleteTextTransaction,
           "DeleteTextTransaction::MaybeCreateForPreviousCharacter() failed");
       return deleteTextTransaction.forget();
     }
     RefPtr<DeleteTextTransaction> deleteTextTransaction =
         DeleteTextTransaction::MaybeCreateForNextCharacter(
-            *this, *point.ContainerAsText(), point.Offset());
+            *this, *point.ContainerAs<Text>(), point.Offset());
     NS_WARNING_ASSERTION(
         deleteTextTransaction,
         "DeleteTextTransaction::MaybeCreateForNextCharacter() failed");
     return deleteTextTransaction.forget();
   }
 
   nsIContent* editableContent = nullptr;
   if (IsHTMLEditor()) {
@@ -4063,17 +4063,17 @@ EditorBase::CreateTransactionForCollapse
     if (!editableContent) {
       NS_WARNING(
           "There was no editable content which is not empty around the "
           "collapsed range");
       return nullptr;
     }
   } else {
     MOZ_ASSERT(point.IsInTextNode());
-    editableContent = point.GetContainerAsContent();
+    editableContent = point.GetContainerAs<nsIContent>();
     if (!editableContent) {
       NS_WARNING("If there was no text node, should've been handled first");
       return nullptr;
     }
   }
 
   if (editableContent->IsText()) {
     if (aHowToHandleCollapsedRange ==
@@ -4302,17 +4302,17 @@ nsresult EditorBase::DeleteSelectionAsSu
     // XXX And also it seems that we don't need to return error here.
     //     Why don't we just ignore?  `Selection::RemoveAllRanges()` may
     //     have been called by mutation event listeners.
     return NS_ERROR_FAILURE;
   }
   if (IsHTMLEditor() && atNewStartOfSelection.IsInTextNode() &&
       !atNewStartOfSelection.GetContainer()->Length()) {
     nsresult rv = DeleteNodeWithTransaction(
-        MOZ_KnownLive(*atNewStartOfSelection.ContainerAsText()));
+        MOZ_KnownLive(*atNewStartOfSelection.ContainerAs<Text>()));
     if (NS_FAILED(rv)) {
       NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
       return rv;
     }
   }
 
   // XXX I don't think that this is necessary in anonymous `<div>` element of
   //     TextEditor since there should be at most one text node and at most
@@ -4400,18 +4400,17 @@ nsresult EditorBase::HandleDropEvent(Dra
       aDropEvent->GetRangeParentContentAndOffset(&dropOffset);
   if (dropOffset < 0) {
     NS_WARNING(
         "DropEvent::GetRangeParentContentAndOffset() returned negative offset");
     return NS_ERROR_FAILURE;
   }
   EditorDOMPoint droppedAt(dropParentContent,
                            AssertedCast<uint32_t>(dropOffset));
-  if (NS_WARN_IF(!droppedAt.IsSet()) ||
-      NS_WARN_IF(!droppedAt.GetContainerAsContent())) {
+  if (NS_WARN_IF(!droppedAt.IsInContentNode())) {
     return NS_ERROR_FAILURE;
   }
 
   // Check if dropping into a selected range.  If so and the source comes from
   // same document, jump through some hoops to determine if mouse is over
   // selection (bail) and whether user wants to copy selection or delete it.
   if (sourceNode && sourceNode->IsEditable() && srcdoc == document) {
     bool isPointInSelection = EditorUtils::IsPointInSelection(
@@ -4465,17 +4464,17 @@ nsresult EditorBase::HandleDropEvent(Dra
         if (selection->IsCollapsed()) {
           editorToDeleteSelection = nullptr;
         }
       }
     }
   }
 
   if (IsInPlaintextMode()) {
-    for (nsIContent* content = droppedAt.GetContainerAsContent(); content;
+    for (nsIContent* content = droppedAt.ContainerAs<nsIContent>(); content;
          content = content->GetParent()) {
       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
       if (formControl && !formControl->AllowDrop()) {
         // Don't allow dropping into a form control that doesn't allow being
         // dropped into.
         return NS_OK;
       }
     }
@@ -4526,36 +4525,37 @@ nsresult EditorBase::HandleDropEvent(Dra
     }
     if (NS_WARN_IF(!rangeAtDropPoint->IsPositioned()) ||
         NS_WARN_IF(!rangeAtDropPoint->GetStartContainer()->IsContent())) {
       editActionData.Abort();
       return NS_ERROR_FAILURE;
     }
     droppedAt = rangeAtDropPoint->StartRef();
     MOZ_ASSERT(droppedAt.IsSetAndValid());
+    MOZ_ASSERT(droppedAt.IsInContentNode());
   }
 
   // Before inserting dropping content, we need to move focus for compatibility
   // with Chrome and firing "beforeinput" event on new editing host.
   RefPtr<Element> focusedElement, newFocusedElement;
   if (IsTextEditor()) {
     newFocusedElement = GetExposedRoot();
     focusedElement = IsActiveInDOMWindow() ? newFocusedElement : nullptr;
   }
   // TODO: We need to add automated tests when dropping something into an
   //       editing host for contenteditable which is in a shadow DOM tree
   //       and its host which is in design mode.
   else if (!AsHTMLEditor()->IsInDesignMode()) {
     focusedElement = AsHTMLEditor()->ComputeEditingHost();
     if (focusedElement &&
-        droppedAt.GetContainerAsContent()->IsInclusiveDescendantOf(
+        droppedAt.ContainerAs<nsIContent>()->IsInclusiveDescendantOf(
             focusedElement)) {
       newFocusedElement = focusedElement;
     } else {
-      newFocusedElement = droppedAt.GetContainerAsContent()->GetEditingHost();
+      newFocusedElement = droppedAt.ContainerAs<nsIContent>()->GetEditingHost();
     }
   }
   // Move selection right now.  Note that this does not move focus because
   // `Selection` moves focus with selection change only when the API caller is
   // JS.  And also this does not notify selection listeners (nor
   // "selectionchange") since we created SelectionBatcher above.
   ErrorResult error;
   SelectionRef().SetStartAndEnd(droppedAt.ToRawRangeBoundary(),
@@ -5280,17 +5280,17 @@ nsresult EditorBase::InitializeSelection
     // XXX If selection is changed during reframe, this doesn't work well!
     const nsRange* firstRange = SelectionRef().GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
     EditorRawDOMPoint atStartOfFirstRange(firstRange->StartRef());
     EditorRawDOMPoint betterInsertionPoint =
         FindBetterInsertionPoint(atStartOfFirstRange);
-    RefPtr<Text> textNode = betterInsertionPoint.GetContainerAsText();
+    RefPtr<Text> textNode = betterInsertionPoint.GetContainerAs<Text>();
     MOZ_ASSERT(textNode,
                "There must be text node if composition string is not empty");
     if (textNode) {
       MOZ_ASSERT(textNode->Length() >= mComposition->XPEndOffsetInTextNode(),
                  "The text node must be different from the old text node");
       RefPtr<TextRangeArray> ranges = mComposition->GetRanges();
       DebugOnly<nsresult> rvIgnored = CompositionTransaction::SetIMESelection(
           *this, textNode, mComposition->XPOffsetInTextNode(),
@@ -5803,31 +5803,32 @@ EditorBase::AutoCaretBidiLevelManager::A
     mFailed = true;
     return;
   }
 
   if (!presContext->BidiEnabled()) {
     return;  // Perform the deletion
   }
 
-  if (!aPointAtCaret.GetContainerAsContent()) {
+  if (!aPointAtCaret.IsInContentNode()) {
     mFailed = true;
     return;
   }
 
   // XXX Not sure whether this requires strong reference here.
   RefPtr<nsFrameSelection> frameSelection =
       aEditorBase.SelectionRef().GetFrameSelection();
   if (NS_WARN_IF(!frameSelection)) {
     mFailed = true;
     return;
   }
 
   nsPrevNextBidiLevels levels = frameSelection->GetPrevNextBidiLevels(
-      aPointAtCaret.GetContainerAsContent(), aPointAtCaret.Offset(), true);
+      aPointAtCaret.template ContainerAs<nsIContent>(), aPointAtCaret.Offset(),
+      true);
 
   mozilla::intl::BidiEmbeddingLevel levelBefore = levels.mLevelBefore;
   mozilla::intl::BidiEmbeddingLevel levelAfter = levels.mLevelAfter;
 
   mozilla::intl::BidiEmbeddingLevel currentCaretLevel =
       frameSelection->GetCaretBidiLevel();
 
   mozilla::intl::BidiEmbeddingLevel levelOfDeletion;
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -190,75 +190,53 @@ class EditorDOMPointBase final {
     mInterlinePosition = aInterlinePosition;
   }
   InterlinePosition GetInterlinePosition() const {
     return IsSet() ? mInterlinePosition : InterlinePosition::Undefined;
   }
 
   /**
    * GetContainer() returns the container node at the point.
-   * GetContainerAs*() returns the container node as specific type.
+   * GetContainerAs() returns the container node as specific type.
    */
   nsINode* GetContainer() const { return mParent; }
-
-  nsIContent* GetContainerAsContent() const {
-    return nsIContent::FromNodeOrNull(mParent);
-  }
-
-  MOZ_NEVER_INLINE_DEBUG nsIContent* ContainerAsContent() const {
-    MOZ_ASSERT(mParent);
-    MOZ_ASSERT(mParent->IsContent());
-    return mParent->AsContent();
-  }
-
-  dom::Element* GetContainerAsElement() const {
-    return dom::Element::FromNodeOrNull(mParent);
+  template <typename ContentNodeType>
+  ContentNodeType* GetContainerAs() const {
+    return ContentNodeType::FromNodeOrNull(mParent);
   }
 
-  MOZ_NEVER_INLINE_DEBUG dom::Element* ContainerAsElement() const {
+  /**
+   * ContainerAs() returns the container node with just casting to the specific
+   * type.  Therefore, callers need to guarantee that the result is not nullptr
+   * nor wrong cast.
+   */
+  template <typename ContentNodeType>
+  ContentNodeType* ContainerAs() const {
     MOZ_ASSERT(mParent);
-    MOZ_ASSERT(mParent->IsElement());
-    return mParent->AsElement();
-  }
-
-  nsStyledElement* GetContainerAsStyledElement() const {
-    return nsStyledElement::FromNodeOrNull(mParent);
-  }
-
-  dom::Text* GetContainerAsText() const {
-    return dom::Text::FromNodeOrNull(mParent);
-  }
-
-  MOZ_NEVER_INLINE_DEBUG dom::Text* ContainerAsText() const {
-    MOZ_ASSERT(mParent);
-    MOZ_ASSERT(IsInTextNode());
-    return mParent->AsText();
+    MOZ_DIAGNOSTIC_ASSERT(ContentNodeType::FromNode(mParent));
+    return static_cast<ContentNodeType*>(GetContainer());
   }
 
   /**
    * GetContainerParent() returns parent of the container node at the point.
    */
   nsINode* GetContainerParent() const {
     return mParent ? mParent->GetParent() : nullptr;
   }
-
-  nsIContent* GetContainerParentAsContent() const {
-    return nsIContent::FromNodeOrNull(GetContainerParent());
-  }
-
-  dom::Element* GetContainerParentAsElement() const {
-    return dom::Element::FromNodeOrNull(GetContainerParent());
+  template <typename ContentNodeType>
+  ContentNodeType* GetContainerParentAs() const {
+    return ContentNodeType::FromNodeOrNull(GetContainerParent());
   }
 
   dom::Element* GetContainerOrContainerParentElement() const {
     if (MOZ_UNLIKELY(!mParent)) {
       return nullptr;
     }
-    return mParent->IsElement() ? ContainerAsElement()
-                                : GetContainerParentAsElement();
+    return mParent->IsElement() ? ContainerAs<dom::Element>()
+                                : GetContainerParentAs<dom::Element>();
   }
 
   /**
    * CanContainerHaveChildren() returns true if the container node can have
    * child nodes.  Otherwise, e.g., when the container is a text node, returns
    * false.
    */
   bool CanContainerHaveChildren() const {
@@ -342,17 +320,17 @@ class EditorDOMPointBase final {
   }
 
   /**
    * GetChildOrContainerIfDataNode() returns the child content node,
    * or container content node if the container is a data node.
    */
   nsIContent* GetChildOrContainerIfDataNode() const {
     if (IsInDataNode()) {
-      return ContainerAsContent();
+      return ContainerAs<nsIContent>();
     }
     return GetChild();
   }
 
   /**
    * GetNextSiblingOfChild() returns next sibling of the child node.
    * If this refers after the last child or the container cannot have children,
    * this returns nullptr with warning.
@@ -404,17 +382,17 @@ class EditorDOMPointBase final {
 
   /**
    * Simple accessors of the character in dom::Text so that when you call
    * these methods, you need to guarantee that the container is a dom::Text.
    */
   MOZ_NEVER_INLINE_DEBUG char16_t Char() const {
     MOZ_ASSERT(IsSetAndValid());
     MOZ_ASSERT(!IsEndOfContainer());
-    return ContainerAsText()->TextFragment().CharAt(mOffset.value());
+    return ContainerAs<dom::Text>()->TextFragment().CharAt(mOffset.value());
   }
   MOZ_NEVER_INLINE_DEBUG bool IsCharASCIISpace() const {
     return nsCRT::IsAsciiSpace(Char());
   }
   MOZ_NEVER_INLINE_DEBUG bool IsCharNBSP() const { return Char() == 0x00A0; }
   MOZ_NEVER_INLINE_DEBUG bool IsCharASCIISpaceOrNBSP() const {
     char16_t ch = Char();
     return nsCRT::IsAsciiSpace(ch) || ch == 0x00A0;
@@ -432,32 +410,32 @@ class EditorDOMPointBase final {
   bool IsCharCollapsibleASCIISpace() const;
   bool IsCharCollapsibleNBSP() const;
   bool IsCharCollapsibleASCIISpaceOrNBSP() const;
 
   MOZ_NEVER_INLINE_DEBUG bool IsCharHighSurrogateFollowedByLowSurrogate()
       const {
     MOZ_ASSERT(IsSetAndValid());
     MOZ_ASSERT(!IsEndOfContainer());
-    return ContainerAsText()
+    return ContainerAs<dom::Text>()
         ->TextFragment()
         .IsHighSurrogateFollowedByLowSurrogateAt(mOffset.value());
   }
   MOZ_NEVER_INLINE_DEBUG bool IsCharLowSurrogateFollowingHighSurrogate() const {
     MOZ_ASSERT(IsSetAndValid());
     MOZ_ASSERT(!IsEndOfContainer());
-    return ContainerAsText()
+    return ContainerAs<dom::Text>()
         ->TextFragment()
         .IsLowSurrogateFollowingHighSurrogateAt(mOffset.value());
   }
 
   MOZ_NEVER_INLINE_DEBUG char16_t PreviousChar() const {
     MOZ_ASSERT(IsSetAndValid());
     MOZ_ASSERT(!IsStartOfContainer());
-    return ContainerAsText()->TextFragment().CharAt(mOffset.value() - 1);
+    return ContainerAs<dom::Text>()->TextFragment().CharAt(mOffset.value() - 1);
   }
   MOZ_NEVER_INLINE_DEBUG bool IsPreviousCharASCIISpace() const {
     return nsCRT::IsAsciiSpace(PreviousChar());
   }
   MOZ_NEVER_INLINE_DEBUG bool IsPreviousCharNBSP() const {
     return PreviousChar() == 0x00A0;
   }
   MOZ_NEVER_INLINE_DEBUG bool IsPreviousCharASCIISpaceOrNBSP() const {
@@ -478,17 +456,17 @@ class EditorDOMPointBase final {
    */
   bool IsPreviousCharCollapsibleASCIISpace() const;
   bool IsPreviousCharCollapsibleNBSP() const;
   bool IsPreviousCharCollapsibleASCIISpaceOrNBSP() const;
 
   MOZ_NEVER_INLINE_DEBUG char16_t NextChar() const {
     MOZ_ASSERT(IsSetAndValid());
     MOZ_ASSERT(!IsAtLastContent() && !IsEndOfContainer());
-    return ContainerAsText()->TextFragment().CharAt(mOffset.value() + 1);
+    return ContainerAs<dom::Text>()->TextFragment().CharAt(mOffset.value() + 1);
   }
   MOZ_NEVER_INLINE_DEBUG bool IsNextCharASCIISpace() const {
     return nsCRT::IsAsciiSpace(NextChar());
   }
   MOZ_NEVER_INLINE_DEBUG bool IsNextCharNBSP() const {
     return NextChar() == 0x00A0;
   }
   MOZ_NEVER_INLINE_DEBUG bool IsNextCharASCIISpaceOrNBSP() const {
@@ -659,17 +637,17 @@ class EditorDOMPointBase final {
    * ParentPoint() returns a point whose child is the container.
    */
   template <typename EditorDOMPointType = SelfType>
   EditorDOMPointType ParentPoint() const {
     MOZ_ASSERT(mParent);
     if (MOZ_UNLIKELY(!mParent) || !mParent->IsContent()) {
       return EditorDOMPointType();
     }
-    return EditorDOMPointType(ContainerAsContent());
+    return EditorDOMPointType(ContainerAs<nsIContent>());
   }
 
   /**
    * NextPoint() and PreviousPoint() returns next/previous DOM point in
    * the container.
    */
   template <typename EditorDOMPointType = SelfType>
   EditorDOMPointType NextPoint() const {
@@ -1094,23 +1072,23 @@ class EditorDOMPointBase final {
     RefPtr<nsRange> range = nsRange::Create(boundary, boundary, aRv);
     if (MOZ_UNLIKELY(aRv.Failed() || !range)) {
       return nullptr;
     }
     return range.forget();
   }
 
   EditorDOMPointInText GetAsInText() const {
-    return IsInTextNode() ? EditorDOMPointInText(ContainerAsText(), Offset(),
-                                                 mInterlinePosition)
+    return IsInTextNode() ? EditorDOMPointInText(ContainerAs<dom::Text>(),
+                                                 Offset(), mInterlinePosition)
                           : EditorDOMPointInText();
   }
   MOZ_NEVER_INLINE_DEBUG EditorDOMPointInText AsInText() const {
     MOZ_ASSERT(IsInTextNode());
-    return EditorDOMPointInText(ContainerAsText(), Offset(),
+    return EditorDOMPointInText(ContainerAs<dom::Text>(), Offset(),
                                 mInterlinePosition);
   }
 
   template <typename A, typename B>
   bool IsBefore(const EditorDOMPointBase<A, B>& aOther) const {
     if (!IsSetAndValid() || !aOther.IsSetAndValid()) {
       return false;
     }
@@ -1296,29 +1274,29 @@ class EditorDOMRangeBase final {
   MOZ_NEVER_INLINE_DEBUG EditorDOMRangeInTexts AsInTexts() const {
     MOZ_ASSERT(IsInTextNodes());
     return EditorDOMRangeInTexts(mStart.AsInText(), mEnd.AsInText());
   }
 
   bool EnsureNotInNativeAnonymousSubtree() {
     if (mStart.IsInNativeAnonymousSubtree()) {
       nsIContent* parent = nullptr;
-      for (parent = mStart.ContainerAsContent()
+      for (parent = mStart.template ContainerAs<nsIContent>()
                         ->GetClosestNativeAnonymousSubtreeRootParent();
            parent && parent->IsInNativeAnonymousSubtree();
            parent = parent->GetClosestNativeAnonymousSubtreeRootParent()) {
       }
       if (MOZ_UNLIKELY(!parent)) {
         return false;
       }
       mStart.Set(parent);
     }
     if (mEnd.IsInNativeAnonymousSubtree()) {
       nsIContent* parent = nullptr;
-      for (parent = mEnd.ContainerAsContent()
+      for (parent = mEnd.template ContainerAs<nsIContent>()
                         ->GetClosestNativeAnonymousSubtreeRootParent();
            parent && parent->IsInNativeAnonymousSubtree();
            parent = parent->GetClosestNativeAnonymousSubtreeRootParent()) {
       }
       if (MOZ_UNLIKELY(!parent)) {
         return false;
       }
       mEnd.SetAfter(parent);
--- a/editor/libeditor/EditorUtils.cpp
+++ b/editor/libeditor/EditorUtils.cpp
@@ -297,164 +297,164 @@ EditorUtils::CreateTransferableForPlainT
  * mozilla::EditorDOMPointBase
  *****************************************************************************/
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharCollapsibleASCIISpace);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleASCIISpace() const {
   if (IsCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsCharASCIISpace() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharCollapsibleNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleNBSP() const {
   // TODO: Perhaps, we should return false if neither previous char nor
   //       next char is collapsible white-space or NBSP.
   return IsCharNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
                                              IsCharCollapsibleASCIISpaceOrNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleASCIISpaceOrNBSP() const {
   if (IsCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsCharASCIISpaceOrNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsPreviousCharCollapsibleASCIISpace);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleASCIISpace() const {
   if (IsPreviousCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsPreviousCharASCIISpace() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
                                              IsPreviousCharCollapsibleNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleNBSP() const {
   return IsPreviousCharNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsPreviousCharCollapsibleASCIISpaceOrNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleASCIISpaceOrNBSP()
     const {
   if (IsPreviousCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsPreviousCharASCIISpaceOrNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
                                              IsNextCharCollapsibleASCIISpace);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleASCIISpace() const {
   if (IsNextCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsNextCharASCIISpace() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsNextCharCollapsibleNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleNBSP() const {
   return IsNextCharNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsNextCharCollapsibleASCIISpaceOrNBSP);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleASCIISpaceOrNBSP() const {
   if (IsNextCharNewLine()) {
-    return !EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+    return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
   }
   return IsNextCharASCIISpaceOrNBSP() &&
-         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAsText());
+         !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharPreformattedNewLine);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsCharPreformattedNewLine() const {
   return IsCharNewLine() &&
-         EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsCharPreformattedNewLineCollapsedWithWhiteSpaces);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<
     PT, CT>::IsCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
   return IsCharNewLine() &&
-         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
                                              IsPreviousCharPreformattedNewLine);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsPreviousCharPreformattedNewLine() const {
   return IsPreviousCharNewLine() &&
-         EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsPreviousCharPreformattedNewLineCollapsedWithWhiteSpaces);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<
     PT, CT>::IsPreviousCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
   return IsPreviousCharNewLine() &&
-         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
                                              IsNextCharPreformattedNewLine);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<PT, CT>::IsNextCharPreformattedNewLine() const {
   return IsNextCharNewLine() &&
-         EditorUtils::IsNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
 }
 
 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
     bool, IsNextCharPreformattedNewLineCollapsedWithWhiteSpaces);
 
 template <typename PT, typename CT>
 bool EditorDOMPointBase<
     PT, CT>::IsNextCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
   return IsNextCharNewLine() &&
-         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAsText());
+         EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
 }
 
 /******************************************************************************
  * mozilla::CreateNodeResultBase
  *****************************************************************************/
 
 NS_INSTANTIATE_CREATE_NODE_RESULT_CONST_METHOD(
     nsresult, SuggestCaretPointTo, const EditorBase& aEditorBase,
--- a/editor/libeditor/HTMLEditHelpers.h
+++ b/editor/libeditor/HTMLEditHelpers.h
@@ -488,17 +488,17 @@ class MOZ_STACK_CLASS SplitNodeResult fi
   /**
    * Returns original content node which is (or is just tried to be) split.
    */
   MOZ_KNOWN_LIVE nsIContent* GetOriginalContent() const {
     MOZ_ASSERT(isOk());
     if (mGivenSplitPoint.IsSet()) {
       // Different from previous/next content, if the creator didn't split a
       // node, the container of the split point is the original node.
-      return mGivenSplitPoint.GetContainerAsContent();
+      return mGivenSplitPoint.GetContainerAs<nsIContent>();
     }
     if (mDirection == SplitNodeDirection::LeftNodeIsNewOne) {
       return mNextNode ? mNextNode : mPreviousNode;
     }
     return mPreviousNode ? mPreviousNode : mNextNode;
   }
   template <typename EditorDOMPointType>
   EditorDOMPointType AtOriginalContent() const {
@@ -762,22 +762,22 @@ class MOZ_STACK_CLASS JoinNodesResult fi
   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
   bool Failed() const { return NS_FAILED(mRv); }
   nsresult Rv() const { return mRv; }
   bool Handled() const { return Succeeded(); }
   bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; }
 
   MOZ_KNOWN_LIVE nsIContent* ExistingContent() const {
     MOZ_ASSERT(Succeeded());
-    return mJoinedPoint.ContainerAsContent();
+    return mJoinedPoint.ContainerAs<nsIContent>();
   }
   template <typename EditorDOMPointType>
   EditorDOMPointType AtExistingContent() const {
     MOZ_ASSERT(Succeeded());
-    return EditorDOMPointType(mJoinedPoint.ContainerAsContent());
+    return EditorDOMPointType(mJoinedPoint.ContainerAs<nsIContent>());
   }
 
   MOZ_KNOWN_LIVE nsIContent* RemovedContent() const {
     MOZ_ASSERT(Succeeded());
     return mRemovedContent;
   }
   template <typename EditorDOMPointType>
   EditorDOMPointType AtRemovedContent() const {
@@ -848,39 +848,42 @@ class MOZ_STACK_CLASS SplitRangeOffFromN
     return MOZ_UNLIKELY(mRv == NS_ERROR_EDITOR_DESTROYED);
   }
 
   /**
    * GetLeftContent() returns new created node before the part of quarried out.
    * This may return nullptr if the method didn't split at start edge of
    * the node.
    */
-  nsIContent* GetLeftContent() const { return mLeftContent; }
-  dom::Element* GetLeftContentAsElement() const {
-    return dom::Element::FromNodeOrNull(mLeftContent);
+  MOZ_KNOWN_LIVE nsIContent* GetLeftContent() const { return mLeftContent; }
+  template <typename ContentNodeType>
+  MOZ_KNOWN_LIVE ContentNodeType* GetLeftContentAs() const {
+    return ContentNodeType::FromNodeOrNull(GetLeftContent());
   }
 
   /**
    * GetMiddleContent() returns new created node between left node and right
    * node.  I.e., this is quarried out from the node.  This may return nullptr
    * if the method unwrapped the middle node.
    */
-  nsIContent* GetMiddleContent() const { return mMiddleContent; }
-  dom::Element* GetMiddleContentAsElement() const {
-    return dom::Element::FromNodeOrNull(mMiddleContent);
+  MOZ_KNOWN_LIVE nsIContent* GetMiddleContent() const { return mMiddleContent; }
+  template <typename ContentNodeType>
+  MOZ_KNOWN_LIVE ContentNodeType* GetMiddleContentAs() const {
+    return ContentNodeType::FromNodeOrNull(GetMiddleContent());
   }
 
   /**
    * GetRightContent() returns the right node after the part of quarried out.
    * This may return nullptr it the method didn't split at end edge of the
    * node.
    */
-  nsIContent* GetRightContent() const { return mRightContent; }
-  dom::Element* GetRightContentAsElement() const {
-    return dom::Element::FromNodeOrNull(mRightContent);
+  MOZ_KNOWN_LIVE nsIContent* GetRightContent() const { return mRightContent; }
+  template <typename ContentNodeType>
+  MOZ_KNOWN_LIVE ContentNodeType* GetRightContentAs() const {
+    return ContentNodeType::FromNodeOrNull(GetRightContent());
   }
 
   /**
    * Suggest caret position to aHTMLEditor.
    */
   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SuggestCaretPointTo(
       const HTMLEditor& aHTMLEditor, const SuggestCaretOptions& aOptions) const;
 
@@ -945,19 +948,19 @@ class MOZ_STACK_CLASS SplitRangeOffFromN
 
 #ifdef DEBUG
   ~SplitRangeOffFromNodeResult() {
     MOZ_ASSERT_IF(isOk(), !mCaretPoint.IsSet() || mHandledCaretPoint);
   }
 #endif
 
  private:
-  nsCOMPtr<nsIContent> mLeftContent;
-  nsCOMPtr<nsIContent> mMiddleContent;
-  nsCOMPtr<nsIContent> mRightContent;
+  MOZ_KNOWN_LIVE nsCOMPtr<nsIContent> mLeftContent;
+  MOZ_KNOWN_LIVE nsCOMPtr<nsIContent> mMiddleContent;
+  MOZ_KNOWN_LIVE nsCOMPtr<nsIContent> mRightContent;
 
   // The point which is a good point to put caret from point of view the
   // splitter.
   EditorDOMPoint mCaretPoint;
 
   nsresult mRv;
 
   bool mutable mHandledCaretPoint = false;
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -487,18 +487,18 @@ nsresult HTMLEditor::OnEndHandlingTopLev
         auto pointToAdjust = GetLastIMESelectionEndPoint<EditorDOMPoint>();
         if (!pointToAdjust.IsInContentNode()) {
           // Otherwise, adjust current selection start point.
           pointToAdjust = GetFirstSelectionStartPoint<EditorDOMPoint>();
           if (NS_WARN_IF(!pointToAdjust.IsInContentNode())) {
             return NS_ERROR_FAILURE;
           }
         }
-        if (EditorUtils::IsEditableContent(*pointToAdjust.ContainerAsContent(),
-                                           EditorType::HTML)) {
+        if (EditorUtils::IsEditableContent(
+                *pointToAdjust.ContainerAs<nsIContent>(), EditorType::HTML)) {
           AutoTrackDOMPoint trackPointToAdjust(RangeUpdaterRef(),
                                                &pointToAdjust);
           nsresult rv =
               WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
                   *this, pointToAdjust);
           if (NS_FAILED(rv)) {
             NS_WARNING(
                 "WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt() "
@@ -514,17 +514,17 @@ nsresult HTMLEditor::OnEndHandlingTopLev
         if (NS_WARN_IF(!TopLevelEditSubActionDataRef()
                             .mSelectedRange->IsPositioned())) {
           return NS_ERROR_FAILURE;
         }
 
         EditorDOMPoint atStart =
             TopLevelEditSubActionDataRef().mSelectedRange->StartPoint();
         if (atStart != pointToAdjust && atStart.IsInContentNode() &&
-            EditorUtils::IsEditableContent(*atStart.ContainerAsContent(),
+            EditorUtils::IsEditableContent(*atStart.ContainerAs<nsIContent>(),
                                            EditorType::HTML)) {
           AutoTrackDOMPoint trackPointToAdjust(RangeUpdaterRef(),
                                                &pointToAdjust);
           AutoTrackDOMPoint trackStartPoint(RangeUpdaterRef(), &atStart);
           nsresult rv =
               WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
                   *this, atStart);
           if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
@@ -537,17 +537,17 @@ nsresult HTMLEditor::OnEndHandlingTopLev
         }
         // we only need to handle old selection endpoint if it was different
         // from start
         EditorDOMPoint atEnd =
             TopLevelEditSubActionDataRef().mSelectedRange->EndPoint();
         if (!TopLevelEditSubActionDataRef().mSelectedRange->Collapsed() &&
             atEnd != pointToAdjust && atEnd != atStart &&
             atEnd.IsInContentNode() &&
-            EditorUtils::IsEditableContent(*atEnd.ContainerAsContent(),
+            EditorUtils::IsEditableContent(*atEnd.ContainerAs<nsIContent>(),
                                            EditorType::HTML)) {
           nsresult rv =
               WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(*this,
                                                                         atEnd);
           if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           NS_WARNING_ASSERTION(
@@ -775,17 +775,17 @@ nsresult HTMLEditor::EnsureCaretNotAfter
       !EditorUtils::IsEditableContent(*previousBRElement->GetParent(),
                                       EditorType::HTML) ||
       !HTMLEditUtils::IsInvisibleBRElement(*previousBRElement)) {
     return NS_OK;
   }
 
   const RefPtr<const Element> blockElementAtSelectionStart =
       HTMLEditUtils::GetInclusiveAncestorElement(
-          *atSelectionStart.ContainerAsContent(),
+          *atSelectionStart.ContainerAs<nsIContent>(),
           HTMLEditUtils::ClosestBlockElement);
   const RefPtr<const Element> parentBlockElementOfBRElement =
       HTMLEditUtils::GetAncestorElement(*previousBRElement,
                                         HTMLEditUtils::ClosestBlockElement);
 
   if (!blockElementAtSelectionStart ||
       blockElementAtSelectionStart != parentBlockElementOfBRElement) {
     return NS_OK;
@@ -1052,21 +1052,21 @@ EditActionResult HTMLEditor::HandleInser
   MOZ_ASSERT(pointToInsert.IsSetAndValid());
 
   // If the point is not in an element which can contain text nodes, climb up
   // the DOM tree.
   if (!pointToInsert.IsInTextNode()) {
     while (!HTMLEditUtils::CanNodeContain(*pointToInsert.GetContainer(),
                                           *nsGkAtoms::textTagName)) {
       if (NS_WARN_IF(pointToInsert.GetContainer() == editingHost) ||
-          NS_WARN_IF(!pointToInsert.GetContainerParentAsContent())) {
+          NS_WARN_IF(!pointToInsert.GetContainerParentAs<nsIContent>())) {
         NS_WARNING("Selection start point couldn't have text nodes");
         return EditActionHandled(NS_ERROR_FAILURE);
       }
-      pointToInsert.Set(pointToInsert.ContainerAsContent());
+      pointToInsert.Set(pointToInsert.ContainerAs<nsIContent>());
     }
   }
 
   if (aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
     auto compositionStartPoint =
         GetFirstIMESelectionStartPoint<EditorDOMPoint>();
     if (!compositionStartPoint.IsSet()) {
       compositionStartPoint = pointToInsert;
@@ -1116,17 +1116,17 @@ EditActionResult HTMLEditor::HandleInser
   MOZ_ASSERT(aEditSubAction == EditSubAction::eInsertText);
 
   // find where we are
   EditorDOMPoint currentPoint(pointToInsert);
 
   // is our text going to be PREformatted?
   // We remember this so that we know how to handle tabs.
   const bool isWhiteSpaceCollapsible = !EditorUtils::IsWhiteSpacePreformatted(
-      *pointToInsert.ContainerAsContent());
+      *pointToInsert.ContainerAs<nsIContent>());
 
   // turn off the edit listener: we know how to
   // build the "doc changed range" ourselves, and it's
   // must faster to do it once here than to track all
   // the changes one at a time.
   AutoRestore<bool> disableListener(
       EditSubActionDataRef().mAdjustChangedRangeFromListener);
   EditSubActionDataRef().mAdjustChangedRangeFromListener = false;
@@ -1560,17 +1560,17 @@ EditActionResult HTMLEditor::InsertParag
   auto pointToInsert =
       selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
   if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
     return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   if (IsMailEditor()) {
     if (RefPtr<Element> mailCiteElement = GetMostDistantAncestorMailCiteElement(
-            *pointToInsert.ContainerAsContent())) {
+            *pointToInsert.ContainerAs<nsIContent>())) {
       // Split any mailcites in the way.  Should we abort this if we encounter
       // table cell boundaries?
       Result<EditorDOMPoint, nsresult> atNewBRElementOrError =
           HandleInsertParagraphInMailCiteElement(*mailCiteElement,
                                                  pointToInsert, aEditingHost);
       if (MOZ_UNLIKELY(atNewBRElementOrError.isErr())) {
         NS_WARNING(
             "HTMLEditor::HandleInsertParagraphInMailCiteElement() failed");
@@ -1595,17 +1595,17 @@ EditActionResult HTMLEditor::InsertParag
   // host must be a <body> element and the selection may be outside the body
   // element.  If the selection is outside the editing host, we should not
   // insert new paragraph nor <br> element.
   // XXX Currently, we don't support editing outside <body> element, but Blink
   //     does it.
   if (aEditingHost.GetParentElement() &&
       HTMLEditUtils::IsSimplyEditableNode(*aEditingHost.GetParentElement()) &&
       !nsContentUtils::ContentIsFlattenedTreeDescendantOf(
-          pointToInsert.ContainerAsContent(), &aEditingHost)) {
+          pointToInsert.ContainerAs<nsIContent>(), &aEditingHost)) {
     return EditActionHandled(NS_ERROR_EDITOR_NO_EDITABLE_RANGE);
   }
 
   auto InsertLineBreakInstead =
       [](const Element* aEditableBlockElement,
          const EditorDOMPoint& aCandidatePointToSplit,
          ParagraphSeparator aDefaultParagraphSeparator,
          const Element& aEditingHost) {
@@ -1650,17 +1650,17 @@ EditActionResult HTMLEditor::InsertParag
         return true;
       };
 
   // Look for the nearest parent block.  However, don't return error even if
   // there is no block parent here because in such case, i.e., editing host
   // is an inline element, we should insert <br> simply.
   RefPtr<Element> editableBlockElement =
       HTMLEditUtils::GetInclusiveAncestorElement(
-          *pointToInsert.ContainerAsContent(),
+          *pointToInsert.ContainerAs<nsIContent>(),
           HTMLEditUtils::ClosestEditableBlockElement);
 
   // If we cannot insert a <p>/<div> element at the selection, we should insert
   // a <br> element or a linefeed instead.
   const ParagraphSeparator separator = GetDefaultParagraphSeparator();
   if (InsertLineBreakInstead(editableBlockElement, pointToInsert, separator,
                              aEditingHost)) {
     // For backward compatibility, we should not insert a linefeed if
@@ -1755,17 +1755,17 @@ EditActionResult HTMLEditor::InsertParag
     pointToInsert = selectionRanges.GetFirstRangeStartPoint<EditorDOMPoint>();
     if (NS_WARN_IF(!pointToInsert.IsInContentNode())) {
       return EditActionIgnored(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
     }
     MOZ_ASSERT(pointToInsert.IsSetAndValid());
     blockElementToPutCaret = suggestBlockElementToPutCaretOrError.unwrap();
 
     editableBlockElement = HTMLEditUtils::GetInclusiveAncestorElement(
-        *pointToInsert.ContainerAsContent(),
+        *pointToInsert.ContainerAs<nsIContent>(),
         HTMLEditUtils::ClosestEditableBlockElement);
     if (NS_WARN_IF(!editableBlockElement)) {
       return EditActionIgnored(NS_ERROR_UNEXPECTED);
     }
     if (NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(*editableBlockElement))) {
       // Didn't create a new block for some reason, fall back to <br>
       CreateElementResult insertBRElementResult =
           HandleInsertBRElement(pointToInsert, aEditingHost);
@@ -2114,17 +2114,17 @@ Result<EditorDOMPoint, nsresult> HTMLEdi
       NS_WARN_IF(!pointToInsert.IsInContentNode())) {
     return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
   MOZ_ASSERT(pointToInsert.IsSetAndValid());
 
   // The node may not be able to have a text node so that we need to check it
   // here.
   if (!pointToInsert.IsInTextNode() &&
-      !HTMLEditUtils::CanNodeContain(*pointToInsert.ContainerAsContent(),
+      !HTMLEditUtils::CanNodeContain(*pointToInsert.ContainerAs<nsIContent>(),
                                      *nsGkAtoms::textTagName)) {
     NS_WARNING(
         "HTMLEditor::HandleInsertLinefeed() couldn't insert a linefeed because "
         "the insertion position couldn't have text nodes");
     return Err(NS_ERROR_EDITOR_NO_EDITABLE_RANGE);
   }
 
   AutoRestore<bool> disableListener(
@@ -2248,17 +2248,17 @@ HTMLEditor::HandleInsertParagraphInMailC
     if (forwardScanFromPointToSplitResult.ReachedBRElement() &&
         forwardScanFromPointToSplitResult.BRElementPtr() != &aMailCiteElement &&
         aMailCiteElement.Contains(
             forwardScanFromPointToSplitResult.BRElementPtr())) {
       pointToSplit =
           forwardScanFromPointToSplitResult.PointAfterContent<EditorDOMPoint>();
     }
 
-    if (NS_WARN_IF(!pointToSplit.GetContainerAsContent())) {
+    if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
       return SplitNodeResult(NS_ERROR_FAILURE);
     }
 
     SplitNodeResult splitResult =
         SplitNodeDeepWithTransaction(aMailCiteElement, pointToSplit,
                                      SplitAtEdges::eDoNotCreateEmptyContainer);
     if (splitResult.isErr()) {
       NS_WARNING(
@@ -2558,56 +2558,57 @@ void HTMLEditor::ExtendRangeToDeleteWith
       WSRunScanner::GetInclusiveNextEditableCharPoint(editingHost,
                                                       aEndToDelete);
   // Blink-compat: Normalize white-spaces in first node only when not removing
   //               its last character or no text nodes follow the first node.
   //               If removing last character of first node and there are
   //               following text nodes, white-spaces in following text node are
   //               normalized instead.
   const bool removingLastCharOfStartNode =
-      aStartToDelete.ContainerAsText() != aEndToDelete.ContainerAsText() ||
+      aStartToDelete.ContainerAs<Text>() != aEndToDelete.ContainerAs<Text>() ||
       (aEndToDelete.IsEndOfContainer() && followingCharPoint.IsSet());
   const bool maybeNormalizePrecedingWhiteSpaces =
       !removingLastCharOfStartNode && precedingCharPoint.IsSet() &&
       !precedingCharPoint.IsEndOfContainer() &&
-      precedingCharPoint.ContainerAsText() ==
-          aStartToDelete.ContainerAsText() &&
+      precedingCharPoint.ContainerAs<Text>() ==
+          aStartToDelete.ContainerAs<Text>() &&
       precedingCharPoint.IsCharCollapsibleASCIISpaceOrNBSP();
   const bool maybeNormalizeFollowingWhiteSpaces =
       followingCharPoint.IsSet() && !followingCharPoint.IsEndOfContainer() &&
-      (followingCharPoint.ContainerAsText() == aEndToDelete.ContainerAsText() ||
+      (followingCharPoint.ContainerAs<Text>() ==
+           aEndToDelete.ContainerAs<Text>() ||
        removingLastCharOfStartNode) &&
       followingCharPoint.IsCharCollapsibleASCIISpaceOrNBSP();
 
   if (!maybeNormalizePrecedingWhiteSpaces &&
       !maybeNormalizeFollowingWhiteSpaces) {
     return;  // There are no white-spaces.
   }
 
   // Next, consider the range to normalize.
   EditorDOMPointInText startToNormalize, endToNormalize;
   if (maybeNormalizePrecedingWhiteSpaces) {
     Maybe<uint32_t> previousCharOffsetOfWhiteSpaces =
         HTMLEditUtils::GetPreviousNonCollapsibleCharOffset(
             precedingCharPoint, {WalkTextOption::TreatNBSPsCollapsible});
-    startToNormalize.Set(precedingCharPoint.ContainerAsText(),
+    startToNormalize.Set(precedingCharPoint.ContainerAs<Text>(),
                          previousCharOffsetOfWhiteSpaces.isSome()
                              ? previousCharOffsetOfWhiteSpaces.value() + 1
                              : 0);
     MOZ_ASSERT(!startToNormalize.IsEndOfContainer());
   }
   if (maybeNormalizeFollowingWhiteSpaces) {
     Maybe<uint32_t> nextCharOffsetOfWhiteSpaces =
         HTMLEditUtils::GetInclusiveNextNonCollapsibleCharOffset(
             followingCharPoint, {WalkTextOption::TreatNBSPsCollapsible});
     if (nextCharOffsetOfWhiteSpaces.isSome()) {
-      endToNormalize.Set(followingCharPoint.ContainerAsText(),
+      endToNormalize.Set(followingCharPoint.ContainerAs<Text>(),
                          nextCharOffsetOfWhiteSpaces.value());
     } else {
-      endToNormalize.SetToEndOf(followingCharPoint.ContainerAsText());
+      endToNormalize.SetToEndOf(followingCharPoint.ContainerAs<Text>());
     }
     MOZ_ASSERT(!endToNormalize.IsStartOfContainer());
   }
 
   // Next, retrieve surrounding information of white-space sequence.
   // If we're removing first text node's last character, we need to
   // normalize white-spaces starts from another text node.  In this case,
   // we need to lie for avoiding assertion in GenerateWhiteSpaceSequence().
@@ -2618,30 +2619,31 @@ void HTMLEditor::ExtendRangeToDeleteWith
                 startToNormalize.IsSet() ? startToNormalize : aStartToDelete);
   CharPointData nextCharPointData =
       GetInclusiveNextCharPointDataForNormalizingWhiteSpaces(
           endToNormalize.IsSet() ? endToNormalize : aEndToDelete);
 
   // Next, compute number of white-spaces in start/end node.
   uint32_t lengthInStartNode = 0, lengthInEndNode = 0;
   if (startToNormalize.IsSet()) {
-    MOZ_ASSERT(startToNormalize.ContainerAsText() ==
-               aStartToDelete.ContainerAsText());
+    MOZ_ASSERT(startToNormalize.ContainerAs<Text>() ==
+               aStartToDelete.ContainerAs<Text>());
     lengthInStartNode = aStartToDelete.Offset() - startToNormalize.Offset();
     MOZ_ASSERT(lengthInStartNode);
   }
   if (endToNormalize.IsSet()) {
     lengthInEndNode =
-        endToNormalize.ContainerAsText() == aEndToDelete.ContainerAsText()
+        endToNormalize.ContainerAs<Text>() == aEndToDelete.ContainerAs<Text>()
             ? endToNormalize.Offset() - aEndToDelete.Offset()
             : endToNormalize.Offset();
     MOZ_ASSERT(lengthInEndNode);
     // If we normalize white-spaces in a text node, we can replace all of them
     // with one ReplaceTextTransaction.
-    if (endToNormalize.ContainerAsText() == aStartToDelete.ContainerAsText()) {
+    if (endToNormalize.ContainerAs<Text>() ==
+        aStartToDelete.ContainerAs<Text>()) {
       lengthInStartNode += lengthInEndNode;
       lengthInEndNode = 0;
     }
   }
 
   MOZ_ASSERT(lengthInStartNode + lengthInEndNode);
 
   // Next, generate normalized white-spaces.
@@ -2709,85 +2711,85 @@ HTMLEditor::DeleteTextAndNormalizeSurrou
   // be replaced, we need to do nothing here.
   if (startToDelete == endToDelete) {
     return aStartToDelete.To<EditorDOMPoint>();
   }
 
   // Note that the container text node of startToDelete may be removed from
   // the tree if it becomes empty.  Therefore, we need to track the point.
   EditorDOMPoint newCaretPosition;
-  if (aStartToDelete.ContainerAsText() == aEndToDelete.ContainerAsText()) {
+  if (aStartToDelete.ContainerAs<Text>() == aEndToDelete.ContainerAs<Text>()) {
     newCaretPosition = aEndToDelete.To<EditorDOMPoint>();
   } else if (aDeleteDirection == DeleteDirection::Forward) {
-    newCaretPosition.SetToEndOf(aStartToDelete.ContainerAsText());
+    newCaretPosition.SetToEndOf(aStartToDelete.ContainerAs<Text>());
   } else {
-    newCaretPosition.Set(aEndToDelete.ContainerAsText(), 0u);
+    newCaretPosition.Set(aEndToDelete.ContainerAs<Text>(), 0u);
   }
 
   // Then, modify the text nodes in the range.
   while (true) {
     AutoTrackDOMPoint trackingNewCaretPosition(RangeUpdaterRef(),
                                                &newCaretPosition);
     // Use ReplaceTextTransaction if we need to normalize white-spaces in
     // the first text node.
     if (!normalizedWhiteSpacesInFirstNode.IsEmpty()) {
-      EditorDOMPoint trackingEndToDelete(endToDelete.ContainerAsText(),
+      EditorDOMPoint trackingEndToDelete(endToDelete.ContainerAs<Text>(),
                                          endToDelete.Offset());
       {
         AutoTrackDOMPoint trackEndToDelete(RangeUpdaterRef(),
                                            &trackingEndToDelete);
         uint32_t lengthToReplaceInFirstTextNode =
-            startToDelete.ContainerAsText() ==
-                    trackingEndToDelete.ContainerAsText()
+            startToDelete.ContainerAs<Text>() ==
+                    trackingEndToDelete.ContainerAs<Text>()
                 ? trackingEndToDelete.Offset() - startToDelete.Offset()
-                : startToDelete.ContainerAsText()->TextLength() -
+                : startToDelete.ContainerAs<Text>()->TextLength() -
                       startToDelete.Offset();
         nsresult rv = ReplaceTextWithTransaction(
-            MOZ_KnownLive(*startToDelete.ContainerAsText()),
+            MOZ_KnownLive(*startToDelete.ContainerAs<Text>()),
             startToDelete.Offset(), lengthToReplaceInFirstTextNode,
             normalizedWhiteSpacesInFirstNode);
         if (NS_FAILED(rv)) {
           NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed");
           return Err(rv);
         }
-        if (startToDelete.ContainerAsText() ==
-            trackingEndToDelete.ContainerAsText()) {
+        if (startToDelete.ContainerAs<Text>() ==
+            trackingEndToDelete.ContainerAs<Text>()) {
           MOZ_ASSERT(normalizedWhiteSpacesInLastNode.IsEmpty());
           break;  // There is no more text which we need to delete.
         }
       }
       if (MayHaveMutationEventListeners(
               NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED) &&
           (NS_WARN_IF(!trackingEndToDelete.IsSetAndValid()) ||
            NS_WARN_IF(!trackingEndToDelete.IsInTextNode()))) {
         return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
       }
       MOZ_ASSERT(trackingEndToDelete.IsInTextNode());
-      endToDelete.Set(trackingEndToDelete.ContainerAsText(),
+      endToDelete.Set(trackingEndToDelete.ContainerAs<Text>(),
                       trackingEndToDelete.Offset());
       // If the remaining range was modified by mutation event listener,
       // we should stop handling the deletion.
       startToDelete =
-          EditorDOMPointInText::AtEndOf(*startToDelete.ContainerAsText());
+          EditorDOMPointInText::AtEndOf(*startToDelete.ContainerAs<Text>());
       if (MayHaveMutationEventListeners(
               NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED) &&
           NS_WARN_IF(!startToDelete.IsBefore(endToDelete))) {
         return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
       }
     }
     // Delete ASCII whiteSpaces in the range simpley if there are some text
     // nodes which we don't need to replace their text.
     if (normalizedWhiteSpacesInLastNode.IsEmpty() ||
-        startToDelete.ContainerAsText() != endToDelete.ContainerAsText()) {
+        startToDelete.ContainerAs<Text>() != endToDelete.ContainerAs<Text>()) {
       // If we need to replace text in the last text node, we should
       // delete text before its previous text node.
       EditorDOMPointInText endToDeleteExceptReplaceRange =
           normalizedWhiteSpacesInLastNode.IsEmpty()
               ? endToDelete
-              : EditorDOMPointInText(endToDelete.ContainerAsText(), 0);
+              : EditorDOMPointInText(endToDelete.ContainerAs<Text>(), 0);
       if (startToDelete != endToDeleteExceptReplaceRange) {
         nsresult rv = DeleteTextAndTextNodesWithTransaction(
             startToDelete, endToDeleteExceptReplaceRange, aTreatEmptyTextNodes);
         if (NS_FAILED(rv)) {
           NS_WARNING(
               "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
           return Err(rv);
         }
@@ -2807,21 +2809,21 @@ HTMLEditor::DeleteTextAndNormalizeSurrou
         // Then, replace the text in the last text node.
         startToDelete = endToDeleteExceptReplaceRange;
       }
     }
 
     // Replace ASCII whiteSpaces in the range and following character in the
     // last text node.
     MOZ_ASSERT(!normalizedWhiteSpacesInLastNode.IsEmpty());
-    MOZ_ASSERT(startToDelete.ContainerAsText() ==
-               endToDelete.ContainerAsText());
+    MOZ_ASSERT(startToDelete.ContainerAs<Text>() ==
+               endToDelete.ContainerAs<Text>());
     nsresult rv = ReplaceTextWithTransaction(
-        MOZ_KnownLive(*startToDelete.ContainerAsText()), startToDelete.Offset(),
-        endToDelete.Offset() - startToDelete.Offset(),
+        MOZ_KnownLive(*startToDelete.ContainerAs<Text>()),
+        startToDelete.Offset(), endToDelete.Offset() - startToDelete.Offset(),
         normalizedWhiteSpacesInLastNode);
     if (NS_FAILED(rv)) {
       NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed");
       return Err(rv);
     }
     break;
   }
 
@@ -2833,17 +2835,17 @@ HTMLEditor::DeleteTextAndNormalizeSurrou
     return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
 
   // Look for leaf node to put caret if we remove some empty inline ancestors
   // at new caret position.
   if (!newCaretPosition.IsInTextNode()) {
     if (const Element* editableBlockElementOrInlineEditingHost =
             HTMLEditUtils::GetInclusiveAncestorElement(
-                *newCaretPosition.ContainerAsContent(),
+                *newCaretPosition.ContainerAs<nsIContent>(),
                 HTMLEditUtils::
                     ClosestEditableBlockElementOrInlineEditingHost)) {
       Element* editingHost = ComputeEditingHost();
       // Try to put caret next to immediately after previous editable leaf.
       nsIContent* previousContent =
           HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
               newCaretPosition, *editableBlockElementOrInlineEditingHost,
               {LeafNodeType::LeafNodeOrNonEditableNode}, editingHost);
@@ -2900,23 +2902,24 @@ HTMLEditor::DeleteTextAndNormalizeSurrou
   return newCaretPosition;
 }
 
 nsresult HTMLEditor::InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
     const EditorDOMPoint& aPointToInsert) {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(aPointToInsert.IsSet());
 
-  if (!aPointToInsert.GetContainerAsContent()) {
+  if (!aPointToInsert.IsInContentNode()) {
     return NS_OK;
   }
 
   // If container of the point is not in a block, we don't need to put a
   // `<br>` element here.
-  if (!HTMLEditUtils::IsBlockElement(*aPointToInsert.ContainerAsContent())) {
+  if (!HTMLEditUtils::IsBlockElement(
+          *aPointToInsert.ContainerAs<nsIContent>())) {
     return NS_OK;
   }
 
   WSRunScanner wsRunScanner(ComputeEditingHost(), aPointToInsert);
   // If the point is not start of a hard line, we don't need to put a `<br>`
   // element here.
   if (!wsRunScanner.StartsFromHardLineBreak()) {
     return NS_OK;
@@ -3433,17 +3436,17 @@ EditActionResult HTMLEditor::ConvertCont
           }
           MOZ_ASSERT(aRanges.HasSavedRanges());
           newListItemElementOrError.IgnoreCaretPointSuggestion();
         }
       } else {
         // If we've not met a list element, set current list element to the
         // parent of current list item element.
         if (!curList) {
-          curList = atContent.GetContainerAsElement();
+          curList = atContent.GetContainerAs<Element>();
           NS_WARNING_ASSERTION(
               HTMLEditUtils::IsAnyListElement(curList),
               "Current list item parent is not a list element");
         }
         // If current list item element is not a child of current list element,
         // move it into current list item.
         else if (atContent.GetContainer() != curList) {
           const MoveNodeResult moveNodeResult =
@@ -3898,17 +3901,17 @@ HTMLEditor::FormatBlockContainerWithTran
         NS_WARNING(
             "HTMLEditor::FormatBlockContainerWithTransaction() couldn't find "
             "block parent because container of the point is not content");
         return Err(NS_ERROR_FAILURE);
       }
       // We are removing blocks (going to "body text")
       const RefPtr<Element> editableBlockElement =
           HTMLEditUtils::GetInclusiveAncestorElement(
-              *pointToInsertBlock.ContainerAsContent(),
+              *pointToInsertBlock.ContainerAs<nsIContent>(),
               HTMLEditUtils::ClosestEditableBlockElement);
       if (!editableBlockElement) {
         NS_WARNING(
             "HTMLEditor::FormatBlockContainerWithTransaction() couldn't find "
             "block parent");
         return Err(NS_ERROR_FAILURE);
       }
       if (!HTMLEditUtils::IsFormatNode(editableBlockElement)) {
@@ -4333,17 +4336,17 @@ nsresult HTMLEditor::HandleCSSIndentArou
   if (aRanges.IsCollapsed()) {
     const auto atCaret = aRanges.GetFirstRangeStartPoint<EditorRawDOMPoint>();
     if (NS_WARN_IF(!atCaret.IsSet())) {
       return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(atCaret.IsInContentNode());
     Element* const editableBlockElement =
         HTMLEditUtils::GetInclusiveAncestorElement(
-            *atCaret.ContainerAsContent(),
+            *atCaret.ContainerAs<nsIContent>(),
             HTMLEditUtils::ClosestEditableBlockElement);
     if (editableBlockElement &&
         HTMLEditUtils::IsListItem(editableBlockElement)) {
       arrayOfContents.AppendElement(*editableBlockElement);
     }
   }
 
   EditorDOMPoint pointToPutCaret;
@@ -5625,17 +5628,17 @@ SplitRangeOffFromNodeResult HTMLEditor::
 
   SplitRangeOffFromNodeResult splitResult =
       SplitRangeOffFromBlock(aBlockElement, aStartOfOutdent, aEndOfOutdent);
   if (splitResult.EditorDestroyed()) {
     NS_WARNING("HTMLEditor::SplitRangeOffFromBlock() failed");
     return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
   }
 
-  if (!splitResult.GetMiddleContentAsElement()) {
+  if (!splitResult.GetMiddleContentAs<Element>()) {
     NS_WARNING(
         "HTMLEditor::SplitRangeOffFromBlock() didn't return middle content");
     splitResult.IgnoreCaretPointSuggestion();
     return SplitRangeOffFromNodeResult(NS_ERROR_FAILURE);
   }
 
   if (splitResult.isOk()) {
     nsresult rv = splitResult.SuggestCaretPointTo(
@@ -5650,42 +5653,45 @@ SplitRangeOffFromNodeResult HTMLEditor::
                          "SplitRangeOffFromNodeResult::SuggestCaretPointTo() "
                          "failed, but ignored");
   } else {
     NS_WARNING(
         "HTMLEditor::SplitRangeOffFromBlock() failed, but might be ignored");
   }
 
   if (aBlockIndentedWith == BlockIndentedWith::HTML) {
+    // MOZ_KnownLive: perhaps, it does not work with template methods.
     Result<EditorDOMPoint, nsresult> unwrapBlockElementResult =
         RemoveBlockContainerWithTransaction(
-            MOZ_KnownLive(*splitResult.GetMiddleContentAsElement()));
+            MOZ_KnownLive(*splitResult.GetMiddleContentAs<Element>()));
     if (MOZ_UNLIKELY(unwrapBlockElementResult.isErr())) {
       NS_WARNING("HTMLEditor::RemoveBlockContainerWithTransaction() failed");
       return SplitRangeOffFromNodeResult(unwrapBlockElementResult.inspectErr());
     }
     const EditorDOMPoint& pointToPutCaret = unwrapBlockElementResult.inspect();
     if (AllowsTransactionsToChangeSelection() && pointToPutCaret.IsSet()) {
       nsresult rv = CollapseSelectionTo(pointToPutCaret);
       if (NS_FAILED(rv)) {
         NS_WARNING("EditorBase::CollapseSelectionTo() failed");
         return SplitRangeOffFromNodeResult(rv);
       }
     }
     return SplitRangeOffFromNodeResult(splitResult.GetLeftContent(), nullptr,
                                        splitResult.GetRightContent());
   }
 
-  if (!splitResult.GetMiddleContentAsElement()) {
+  if (!splitResult.GetMiddleContentAs<Element>()) {
     return splitResult;
   }
 
+  // MOZ_KnownLive: perhaps, it does not work with template methods.
   const Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
-      ChangeMarginStart(MOZ_KnownLive(*splitResult.GetMiddleContentAsElement()),
-                        ChangeMargin::Decrease, aEditingHost);
+      ChangeMarginStart(
+          MOZ_KnownLive(*splitResult.GetMiddleContentAs<Element>()),
+          ChangeMargin::Decrease, aEditingHost);
   if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
     NS_WARNING("HTMLEditor::ChangeMarginStart(ChangeMargin::Decrease) failed");
     return SplitRangeOffFromNodeResult(pointToPutCaretOrError.inspectErr());
   }
   if (AllowsTransactionsToChangeSelection() &&
       pointToPutCaretOrError.inspect().IsSet()) {
     nsresult rv = CollapseSelectionTo(pointToPutCaretOrError.inspect());
     if (NS_FAILED(rv)) {
@@ -5803,34 +5809,34 @@ Result<EditorDOMPoint, nsresult> HTMLEdi
 
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
     EditorDOMPoint pointToInsertTextNode(pointToPutCaret);
     if (pointToInsertTextNode.IsInTextNode()) {
       // if we are in a text node, split it
       SplitNodeResult splitTextNodeResult = SplitNodeDeepWithTransaction(
-          MOZ_KnownLive(*pointToInsertTextNode.GetContainerAsText()),
+          MOZ_KnownLive(*pointToInsertTextNode.ContainerAs<Text>()),
           pointToInsertTextNode, SplitAtEdges::eAllowToCreateEmptyContainer);
       if (splitTextNodeResult.isErr()) {
         NS_WARNING(
             "HTMLEditor::SplitNodeDeepWithTransaction(SplitAtEdges::"
             "eAllowToCreateEmptyContainer) failed");
         return Err(splitTextNodeResult.unwrapErr());
       }
       splitTextNodeResult.MoveCaretPointTo(
           pointToPutCaret, *this,
           {SuggestCaret::OnlyIfHasSuggestion,
            SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
       pointToInsertTextNode =
           splitTextNodeResult.AtSplitPoint<EditorDOMPoint>();
     }
     if (!pointToInsertTextNode.IsInContentNode() ||
         !HTMLEditUtils::IsContainerNode(
-            *pointToInsertTextNode.ContainerAsContent())) {
+            *pointToInsertTextNode.ContainerAs<nsIContent>())) {
       return pointToPutCaret;
     }
     RefPtr<Text> newEmptyTextNode = CreateTextNode(u""_ns);
     if (!newEmptyTextNode) {
       NS_WARNING("EditorBase::CreateTextNode() failed");
       return Err(NS_ERROR_FAILURE);
     }
     CreateTextResult insertNewTextNodeResult = InsertNodeWithTransaction<Text>(
@@ -5857,17 +5863,17 @@ Result<EditorDOMPoint, nsresult> HTMLEdi
         // which is computed above.
         MOZ_ASSERT(pointToPutCaret.IsSet());
         wrapTextInBigOrSmallElementResult.IgnoreCaretPointSuggestion();
       }
     }
 
     while (item) {
       Result<EditorDOMPoint, nsresult> setStyleResult = SetInlinePropertyOnNode(
-          MOZ_KnownLive(*pointToPutCaret.GetContainerAsContent()),
+          MOZ_KnownLive(*pointToPutCaret.ContainerAs<nsIContent>()),
           MOZ_KnownLive(*item->tag), MOZ_KnownLive(item->attr), item->value);
       if (MOZ_UNLIKELY(setStyleResult.isErr())) {
         NS_WARNING("HTMLEditor::SetInlinePropertyOnNode() failed");
         return Err(setStyleResult.unwrapErr());
       }
       // We don't need to update here because we'll suggest caret position which
       // is computed above.
       MOZ_ASSERT(pointToPutCaret.IsSet());
@@ -6830,17 +6836,17 @@ HTMLEditor::SplitParentInlineElementsAtR
             "eDoNotCreateEmptyContainer) failed");
         return Err(splitEndInlineResult.unwrapErr());
       }
       splitEndInlineResult.MoveCaretPointTo(
           pointToPutCaret, {SuggestCaret::OnlyIfHasSuggestion});
       if (pointToPutCaret.IsInContentNode() &&
           MOZ_UNLIKELY(
               editingHost !=
-              ComputeEditingHost(*pointToPutCaret.GetContainerAsContent()))) {
+              ComputeEditingHost(*pointToPutCaret.ContainerAs<nsIContent>()))) {
         NS_WARNING(
             "HTMLEditor::SplitNodeDeepWithTransaction(SplitAtEdges::"
             "eDoNotCreateEmptyContainer) caused changing editing host");
         return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
       }
       const EditorRawDOMPoint& splitPointAtEnd =
           splitEndInlineResult.AtSplitPoint<EditorRawDOMPoint>();
       if (MOZ_UNLIKELY(!splitPointAtEnd.IsSet())) {
@@ -7159,17 +7165,17 @@ SplitNodeResult HTMLEditor::HandleInsert
     // *same* anchor element across two or more paragraphs in most cases.
     // So, adjust selection start if it's edge of anchor element(s).
     // XXX We don't support white-space collapsing in these cases since it needs
     //     some additional work with WhiteSpaceVisibilityKeeper but it's not
     //     usual case. E.g., |<a href="foo"><b>foo []</b> </a>|
     if (aCandidatePointToSplit.IsStartOfContainer()) {
       EditorDOMPoint candidatePoint(aCandidatePointToSplit);
       for (nsIContent* container =
-               aCandidatePointToSplit.GetContainerAsContent();
+               aCandidatePointToSplit.GetContainerAs<nsIContent>();
            container && container != &aParentDivOrP;
            container = container->GetParent()) {
         if (HTMLEditUtils::IsLink(container)) {
           // Found link should be only in right node.  So, we shouldn't split
           // it.
           candidatePoint.Set(container);
           // Even if we found an anchor element, don't break because DOM API
           // allows to nest anchor elements.
@@ -7195,17 +7201,17 @@ SplitNodeResult HTMLEditor::HandleInsert
       // If there are 2 <br> elements, the first <br> element is visible.  E.g.,
       // |<a href="foo"><b>boo[]<br></b><br></a>|, we should split the <a>
       // element.  Otherwise, E.g., |<a href="foo"><b>boo[]<br></b></a>|,
       // we should not split the <a> element and ignore inline elements in it.
       bool foundBRElement =
           aCandidatePointToSplit.IsBRElementAtEndOfContainer();
       EditorDOMPoint candidatePoint(aCandidatePointToSplit);
       for (nsIContent* container =
-               aCandidatePointToSplit.GetContainerAsContent();
+               aCandidatePointToSplit.GetContainerAs<nsIContent>();
            container && container != &aParentDivOrP;
            container = container->GetParent()) {
         if (HTMLEditUtils::IsLink(container)) {
           // Found link should be only in left node.  So, we shouldn't split it.
           candidatePoint.SetAfter(container);
           // Even if we found an anchor element, don't break because DOM API
           // allows to nest anchor elements.
         }
@@ -7244,17 +7250,17 @@ SplitNodeResult HTMLEditor::HandleInsert
       // keep the inline elements containing this text node.
       // TODO: If the parent of the text node is the splitting paragraph,
       //       obviously we don't need to do this because empty paragraphs will
       //       be treated by SplitParagraphWithTransaction().  In this case, we
       //       just need to update pointToSplit for using the same path as the
       //       previous `if` block.
       brElement =
           HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousSibling(
-              *pointToSplit.ContainerAsText(),
+              *pointToSplit.ContainerAs<Text>(),
               {WalkTreeOption::IgnoreNonEditableNode}));
       if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
           EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
         // If insertParagraph does not create a new paragraph, default to
         // insertLineBreak.
         if (!createNewParagraph) {
           return SplitNodeResult::NotHandled(pointToSplit,
                                              GetSplitNodeDirection());
@@ -7278,28 +7284,28 @@ SplitNodeResult HTMLEditor::HandleInsert
       // If we're splitting the paragraph at end of a text node and there is not
       // following visible <br> element, we need to create a <br> element after
       // the text node to make current style specified by parent inline elements
       // keep in the right paragraph.
       // TODO: Same as above, we don't need to do this if the text node is a
       //       direct child of the paragraph.  For using the simplest path, we
       //       just need to update `pointToSplit` in the case.
       brElement = HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextSibling(
-          *pointToSplit.ContainerAsText(),
+          *pointToSplit.ContainerAs<Text>(),
           {WalkTreeOption::IgnoreNonEditableNode}));
       if (!brElement || HTMLEditUtils::IsInvisibleBRElement(*brElement) ||
           EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
         // If insertParagraph does not create a new paragraph, default to
         // insertLineBreak.
         if (!createNewParagraph) {
           return SplitNodeResult::NotHandled(pointToSplit,
                                              GetSplitNodeDirection());
         }
         const EditorDOMPoint pointToInsertBR =
-            EditorDOMPoint::After(*pointToSplit.ContainerAsText());
+            EditorDOMPoint::After(*pointToSplit.ContainerAs<Text>());
         MOZ_ASSERT(pointToInsertBR.IsSet());
         CreateElementResult insertBRElementResult =
             InsertBRElement(WithTransaction::Yes, pointToInsertBR);
         if (insertBRElementResult.isErr()) {
           NS_WARNING(
               "HTMLEditor::InsertBRElement(WithTransaction::Yes) failed");
           return SplitNodeResult(insertBRElementResult.unwrapErr());
         }
@@ -7364,17 +7370,17 @@ SplitNodeResult HTMLEditor::HandleInsert
       pointToSplit.SetToEndOf(splitParentDivOrPResult.GetPreviousContent());
       if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
         return SplitNodeResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
       }
 
       // We need to put new <br> after the left node if given node was split
       // above.
       const EditorDOMPoint pointToInsertBR =
-          EditorDOMPoint::After(*pointToSplit.ContainerAsContent());
+          EditorDOMPoint::After(*pointToSplit.ContainerAs<nsIContent>());
       MOZ_ASSERT(pointToInsertBR.IsSet());
       CreateElementResult insertBRElementResult =
           InsertBRElement(WithTransaction::Yes, pointToInsertBR);
       if (insertBRElementResult.isErr()) {
         NS_WARNING("HTMLEditor::InsertBRElement(WithTransaction::Yes) failed");
         return SplitNodeResult(insertBRElementResult.unwrapErr());
       }
       // We'll collapse `Selection` to the place suggested by
@@ -8910,31 +8916,31 @@ nsresult HTMLEditor::AdjustCaretPosition
   MOZ_ASSERT(SelectionRef().IsCollapsed());
 
   auto point = GetFirstSelectionStartPoint<EditorDOMPoint>();
   if (NS_WARN_IF(!point.IsInContentNode())) {
     return NS_ERROR_FAILURE;
   }
 
   // If selection start is not editable, climb up the tree until editable one.
-  while (!EditorUtils::IsEditableContent(*point.ContainerAsContent(),
+  while (!EditorUtils::IsEditableContent(*point.ContainerAs<nsIContent>(),
                                          EditorType::HTML)) {
     point.Set(point.GetContainer());
     if (NS_WARN_IF(!point.IsInContentNode())) {
       return NS_ERROR_FAILURE;
     }
   }
 
   // If caret is in empty block element, we need to insert a `<br>` element
   // because the block should have one-line height.
   // XXX Even if only a part of the block is editable, shouldn't we put
   //     caret if the block element is now empty?
   if (Element* const editableBlockElement =
           HTMLEditUtils::GetInclusiveAncestorElement(
-              *point.ContainerAsContent(),
+              *point.ContainerAs<nsIContent>(),
               HTMLEditUtils::ClosestEditableBlockElement)) {
     if (editableBlockElement &&
         HTMLEditUtils::IsEmptyNode(
             *editableBlockElement,
             {EmptyCheckOption::TreatSingleBRElementAsVisible}) &&
         HTMLEditUtils::CanNodeContain(*point.GetContainer(), *nsGkAtoms::br)) {
       Element* bodyOrDocumentElement = GetRoot();
       if (NS_WARN_IF(!bodyOrDocumentElement)) {
@@ -8992,26 +8998,27 @@ nsresult HTMLEditor::AdjustCaretPosition
   if (nsCOMPtr<nsIContent> previousEditableContent =
           HTMLEditUtils::GetPreviousContent(
               point, {WalkTreeOption::IgnoreNonEditableNode}, editingHost)) {
     // If caret and previous editable content are in same block element
     // (even if it's a non-editable element), we should put a padding <br>
     // element at end of the block.
     const Element* const blockElementContainingCaret =
         HTMLEditUtils::GetInclusiveAncestorElement(
-            *point.ContainerAsContent(), HTMLEditUtils::ClosestBlockElement);
+            *point.ContainerAs<nsIContent>(),
+            HTMLEditUtils::ClosestBlockElement);
     const Element* const blockElementContainingPreviousEditableContent =
         HTMLEditUtils::GetAncestorElement(*previousEditableContent,
                                           HTMLEditUtils::ClosestBlockElement);
     // If previous editable content of caret is in same block and a `<br>`
     // element, we need to adjust interline position.
     if (blockElementContainingCaret &&
         blockElementContainingCaret ==
             blockElementContainingPreviousEditableContent &&
-        point.ContainerAsContent()->GetEditingHost() ==
+        point.ContainerAs<nsIContent>()->GetEditingHost() ==
             previousEditableContent->GetEditingHost() &&
         previousEditableContent &&
         previousEditableContent->IsHTMLElement(nsGkAtoms::br)) {
       // If it's an invisible `<br>` element, we need to insert a padding
       // `<br>` element for making empty line have one-line height.
       if (HTMLEditUtils::IsInvisibleBRElement(*previousEditableContent) &&
           !EditorUtils::IsPaddingBRElementForEmptyLastLine(
               *previousEditableContent)) {
@@ -9147,30 +9154,30 @@ nsresult HTMLEditor::RemoveEmptyNodesIn(
     // container, it means that the inclusive ancestor empty element may be
     // created by splitting the left nodes.
     if (aRange.Collapsed() || !aRange.IsInContentNodes() ||
         !aRange.EndRef().IsStartOfContainer()) {
       return aRange.EndRef().ToRawRangeBoundary();
     }
     nsINode* const commonAncestor =
         nsContentUtils::GetClosestCommonInclusiveAncestor(
-            aRange.StartRef().ContainerAsContent(),
-            aRange.EndRef().ContainerAsContent());
+            aRange.StartRef().ContainerAs<nsIContent>(),
+            aRange.EndRef().ContainerAs<nsIContent>());
     if (!commonAncestor) {
       return aRange.EndRef().ToRawRangeBoundary();
     }
     nsIContent* maybeRightContent = nullptr;
     for (nsIContent* content : aRange.EndRef()
-                                   .ContainerAsContent()
+                                   .ContainerAs<nsIContent>()
                                    ->InclusiveAncestorsOfType<nsIContent>()) {
       if (!HTMLEditUtils::IsSimplyEditableNode(*content) ||
           content == commonAncestor) {
         break;
       }
-      if (aRange.StartRef().ContainerAsContent() == content) {
+      if (aRange.StartRef().ContainerAs<nsIContent>() == content) {
         break;
       }
       EmptyCheckOptions options = {EmptyCheckOption::TreatListItemAsVisible,
                                    EmptyCheckOption::TreatTableCellAsVisible};
       if (!HTMLEditUtils::IsBlockElement(*content)) {
         options += EmptyCheckOption::TreatSingleBRElementAsVisible;
       }
       if (!HTMLEditUtils::IsEmptyNode(*content, options)) {
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -697,27 +697,27 @@ bool HTMLEditUtils::ShouldInsertLinefeed
   if (!aPointToInsert.IsInContentNode()) {
     return false;
   }
 
   // closestEditableBlockElement can be nullptr if aEditingHost is an inline
   // element.
   Element* closestEditableBlockElement =
       HTMLEditUtils::GetInclusiveAncestorElement(
-          *aPointToInsert.ContainerAsContent(),
+          *aPointToInsert.ContainerAs<nsIContent>(),
           HTMLEditUtils::ClosestEditableBlockElement);
 
   // If and only if the nearest block is the editing host or its parent,
   // and the outer display value of the editing host is inline, and new
   // line character is preformatted, we should insert a linefeed.
   return (!closestEditableBlockElement ||
           closestEditableBlockElement == &aEditingHost) &&
          HTMLEditUtils::IsDisplayOutsideInline(aEditingHost) &&
          EditorUtils::IsNewLinePreformatted(
-             *aPointToInsert.ContainerAsContent());
+             *aPointToInsert.ContainerAs<nsIContent>());
 }
 
 // We use bitmasks to test containment of elements. Elements are marked to be
 // in certain groups by setting the mGroup member of the `ElementInfo` struct
 // to the corresponding GROUP_ values (OR'ed together). Similarly, elements are
 // marked to allow containment of certain groups by setting the
 // mCanContainGroups member of the `ElementInfo` struct to the corresponding
 // GROUP_ values (OR'ed together).
@@ -1080,17 +1080,18 @@ nsIContent* HTMLEditUtils::GetPreviousCo
       "GetPreviousContent() doesn't assume that the start point is a "
       "data node except text node");
 
   // If we are at the beginning of the node, or it is a text node, then just
   // look before it.
   if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
     if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
         aPoint.IsInContentNode() &&
-        HTMLEditUtils::IsBlockElement(*aPoint.ContainerAsContent())) {
+        HTMLEditUtils::IsBlockElement(
+            *aPoint.template ContainerAs<nsIContent>())) {
       // If we aren't allowed to cross blocks, don't look before this block.
       return nullptr;
     }
     return HTMLEditUtils::GetPreviousContent(*aPoint.GetContainer(), aOptions,
                                              aAncestorLimiter);
   }
 
   // else look before the child at 'aOffset'
@@ -1171,17 +1172,18 @@ nsIContent* HTMLEditUtils::GetNextConten
     return HTMLEditUtils::GetNextContent(*firstLeafContent, aOptions,
                                          aAncestorLimiter);
   }
 
   // unless there isn't one, in which case we are at the end of the node
   // and want the next one.
   if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
       point.IsInContentNode() &&
-      HTMLEditUtils::IsBlockElement(*point.ContainerAsContent())) {
+      HTMLEditUtils::IsBlockElement(
+          *point.template ContainerAs<nsIContent>())) {
     // don't cross out of parent block
     return nullptr;
   }
 
   return HTMLEditUtils::GetNextContent(*point.GetContainer(), aOptions,
                                        aAncestorLimiter);
 }
 
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -375,35 +375,36 @@ class HTMLEditUtils final {
     }
     if (!aPoint.IsInTextNode() || aPoint.IsEndOfContainer() ||
         !aPoint.IsCharPreformattedNewLine()) {
       return false;
     }
     // If there are some other characters in the text node, it's a visible
     // linefeed.
     if (!aPoint.IsAtLastContent()) {
-      if (EditorUtils::IsWhiteSpacePreformatted(*aPoint.ContainerAsText())) {
+      if (EditorUtils::IsWhiteSpacePreformatted(
+              *aPoint.template ContainerAs<Text>())) {
         return true;
       }
       const nsTextFragment& textFragment =
-          aPoint.ContainerAsText()->TextFragment();
+          aPoint.template ContainerAs<Text>()->TextFragment();
       for (uint32_t offset = aPoint.Offset() + 1;
            offset < textFragment.GetLength(); ++offset) {
         char16_t ch = textFragment.CharAt(AssertedCast<int32_t>(offset));
         if (nsCRT::IsAsciiSpace(ch) && ch != HTMLEditUtils::kNewLine) {
           continue;  // ASCII white-space which is collapsed into the linefeed.
         }
         return true;  // There is a visible character after it.
       }
     }
     // If followed by a block boundary without visible content, it's invisible
     // linefeed.
     Element* followingBlockElement =
         HTMLEditUtils::GetElementOfImmediateBlockBoundary(
-            *aPoint.ContainerAsText(), WalkTreeDirection::Forward);
+            *aPoint.template ContainerAs<Text>(), WalkTreeDirection::Forward);
     if (aFollowingBlockElement) {
       *aFollowingBlockElement = followingBlockElement;
     }
     return !followingBlockElement;
   }
   template <typename EditorDOMPointType>
   static bool IsInvisiblePreformattedNewLine(
       const EditorDOMPointType& aPoint,
@@ -523,24 +524,25 @@ class HTMLEditUtils final {
       return false;
     }
     if (!aPoint.IsStartOfContainer() && !aPoint.IsEndOfContainer()) {
       return false;
     }
     // XXX Assuming it's not in an empty text node because it's unrealistic edge
     //     case.
     bool maybeStartOfAnchor = aPoint.IsStartOfContainer();
-    for (EditorRawDOMPoint point(aPoint.ContainerAsContent());
+    for (EditorRawDOMPoint point(aPoint.template ContainerAs<nsIContent>());
          point.IsSet() && (maybeStartOfAnchor ? point.IsStartOfContainer()
                                               : point.IsAtLastContent());
          point = point.ParentPoint()) {
       if (HTMLEditUtils::IsLink(point.GetContainer())) {
         // Now, we're at start or end of <a href>.
         if (aFoundLinkElement) {
-          *aFoundLinkElement = do_AddRef(point.ContainerAsElement()).take();
+          *aFoundLinkElement =
+              do_AddRef(point.template ContainerAs<Element>()).take();
         }
         return true;
       }
     }
     return false;
   }
 
   /**
@@ -814,17 +816,17 @@ class HTMLEditUtils final {
     }
 
     // don't cross any table elements
     if ((!aPoint.IsInContentNode() &&
          !!HTMLEditUtils::GetInclusiveAncestorAnyTableElement(
              *editableContent)) ||
         (HTMLEditUtils::GetInclusiveAncestorAnyTableElement(*editableContent) !=
          HTMLEditUtils::GetInclusiveAncestorAnyTableElement(
-             *aPoint.ContainerAsContent()))) {
+             *aPoint.template ContainerAs<nsIContent>()))) {
       return nullptr;
     }
 
     // otherwise, ok, we have found a good spot to put the selection
     return editableContent;
   }
 
   /**
@@ -1016,36 +1018,37 @@ class HTMLEditUtils final {
     NS_ASSERTION(!aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
                  "Not implemented yet");
 
     if (!aStartPoint.IsInContentNode()) {
       return nullptr;
     }
     if (aStartPoint.IsInTextNode()) {
       return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
-          *aStartPoint.ContainerAsText(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<Text>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
-    if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) {
+    if (!HTMLEditUtils::IsContainerNode(
+            *aStartPoint.template ContainerAs<nsIContent>())) {
       return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
-          *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
 
     nsCOMPtr<nsIContent> nextContent = aStartPoint.GetChild();
     if (!nextContent) {
       if (aStartPoint.GetContainer() == &aCurrentBlock) {
         // We are at end of the block.
         return nullptr;
       }
 
       // We are at end of non-block container
       return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
-          *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
 
     // We have a next node.  If it's a block, return it.
     if (HTMLEditUtils::IsBlockElement(*nextContent)) {
       return nextContent;
     }
     if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
         aStartPoint.GetContainer()->IsEditable() &&
@@ -1154,35 +1157,36 @@ class HTMLEditUtils final {
     NS_ASSERTION(!aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
                  "Not implemented yet");
 
     if (!aStartPoint.IsInContentNode()) {
       return nullptr;
     }
     if (aStartPoint.IsInTextNode()) {
       return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
-          *aStartPoint.ContainerAsText(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<Text>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
-    if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) {
+    if (!HTMLEditUtils::IsContainerNode(
+            *aStartPoint.template ContainerAs<nsIContent>())) {
       return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
-          *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
 
     if (aStartPoint.IsStartOfContainer()) {
       if (aStartPoint.GetContainer() == &aCurrentBlock) {
         // We are at start of the block.
         return nullptr;
       }
 
       // We are at start of non-block container
       return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
-          *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes,
-          aAncestorLimiter);
+          *aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock,
+          aLeafNodeTypes, aAncestorLimiter);
     }
 
     nsCOMPtr<nsIContent> previousContent =
         aStartPoint.GetPreviousSiblingOfChild();
     if (NS_WARN_IF(!previousContent)) {
       return nullptr;
     }
 
@@ -1669,17 +1673,17 @@ class HTMLEditUtils final {
     TreatNBSPsCollapsible,
   };
   using WalkTextOptions = EnumSet<WalkTextOption>;
   static Maybe<uint32_t> GetPreviousNonCollapsibleCharOffset(
       const EditorDOMPointInText& aPoint,
       const WalkTextOptions& aWalkTextOptions = {}) {
     MOZ_ASSERT(aPoint.IsSetAndValid());
     return GetPreviousNonCollapsibleCharOffset(
-        *aPoint.ContainerAsText(), aPoint.Offset(), aWalkTextOptions);
+        *aPoint.ContainerAs<Text>(), aPoint.Offset(), aWalkTextOptions);
   }
   static Maybe<uint32_t> GetPreviousNonCollapsibleCharOffset(
       const Text& aTextNode, uint32_t aOffset,
       const WalkTextOptions& aWalkTextOptions = {}) {
     const bool isWhiteSpaceCollapsible =
         !EditorUtils::IsWhiteSpacePreformatted(aTextNode);
     const bool isNewLineCollapsible =
         !EditorUtils::IsNewLinePreformatted(aTextNode);
@@ -1721,17 +1725,17 @@ class HTMLEditUtils final {
   /**
    * GetNextNonCollapsibleCharOffset() returns offset of next character which is
    * not collapsible white-space characters.
    */
   static Maybe<uint32_t> GetNextNonCollapsibleCharOffset(
       const EditorDOMPointInText& aPoint,
       const WalkTextOptions& aWalkTextOptions = {}) {
     MOZ_ASSERT(aPoint.IsSetAndValid());
-    return GetNextNonCollapsibleCharOffset(*aPoint.ContainerAsText(),
+    return GetNextNonCollapsibleCharOffset(*aPoint.ContainerAs<Text>(),
                                            aPoint.Offset(), aWalkTextOptions);
   }
   static Maybe<uint32_t> GetNextNonCollapsibleCharOffset(
       const Text& aTextNode, uint32_t aOffset,
       const WalkTextOptions& aWalkTextOptions = {}) {
     return GetInclusiveNextNonCollapsibleCharOffset(aTextNode, aOffset + 1,
                                                     aWalkTextOptions);
   }
@@ -1740,17 +1744,17 @@ class HTMLEditUtils final {
    * GetInclusiveNextNonCollapsibleCharOffset() returns offset of inclusive next
    * character which is not collapsible white-space characters.
    */
   static Maybe<uint32_t> GetInclusiveNextNonCollapsibleCharOffset(
       const EditorDOMPointInText& aPoint,
       const WalkTextOptions& aWalkTextOptions = {}) {
     MOZ_ASSERT(aPoint.IsSetAndValid());
     return GetInclusiveNextNonCollapsibleCharOffset(
-        *aPoint.ContainerAsText(), aPoint.Offset(), aWalkTextOptions);
+        *aPoint.ContainerAs<Text>(), aPoint.Offset(), aWalkTextOptions);
   }
   static Maybe<uint32_t> GetInclusiveNextNonCollapsibleCharOffset(
       const Text& aTextNode, uint32_t aOffset,
       const WalkTextOptions& aWalkTextOptions = {}) {
     const bool isWhiteSpaceCollapsible =
         !EditorUtils::IsWhiteSpacePreformatted(aTextNode);
     const bool isNewLineCollapsible =
         !EditorUtils::IsNewLinePreformatted(aTextNode);
@@ -1802,17 +1806,17 @@ class HTMLEditUtils final {
     MOZ_ASSERT(!aPoint.IsEndOfContainer());
     MOZ_ASSERT_IF(
         aWalkTextOptions.contains(WalkTextOption::TreatNBSPsCollapsible),
         aPoint.IsCharCollapsibleASCIISpaceOrNBSP());
     MOZ_ASSERT_IF(
         !aWalkTextOptions.contains(WalkTextOption::TreatNBSPsCollapsible),
         aPoint.IsCharCollapsibleASCIISpace());
     return GetFirstWhiteSpaceOffsetCollapsedWith(
-        *aPoint.ContainerAsText(), aPoint.Offset(), aWalkTextOptions);
+        *aPoint.ContainerAs<Text>(), aPoint.Offset(), aWalkTextOptions);
   }
   static uint32_t GetFirstWhiteSpaceOffsetCollapsedWith(
       const Text& aTextNode, uint32_t aOffset,
       const WalkTextOptions& aWalkTextOptions = {}) {
     MOZ_ASSERT(aOffset < aTextNode.TextLength());
     MOZ_ASSERT_IF(
         aWalkTextOptions.contains(WalkTextOption::TreatNBSPsCollapsible),
         EditorRawDOMPoint(&aTextNode, aOffset)
@@ -1836,20 +1840,21 @@ class HTMLEditUtils final {
    * previous preformatted linefeed if there is and aPoint is in a text node.
    * If the node's linefeed characters are not preformatted or aPoint is not
    * in a text node, this returns unset DOM point.
    */
   template <typename EditorDOMPointType, typename ArgEditorDOMPointType>
   static EditorDOMPointType GetPreviousPreformattedNewLineInTextNode(
       const ArgEditorDOMPointType& aPoint) {
     if (!aPoint.IsInTextNode() || aPoint.IsStartOfContainer() ||
-        !EditorUtils::IsNewLinePreformatted(*aPoint.ContainerAsText())) {
+        !EditorUtils::IsNewLinePreformatted(
+            *aPoint.template ContainerAs<Text>())) {
       return EditorDOMPointType();
     }
-    Text* textNode = aPoint.ContainerAsText();
+    Text* textNode = aPoint.template ContainerAs<Text>();
     const nsTextFragment& textFragment = textNode->TextFragment();
     MOZ_ASSERT(aPoint.Offset() <= textFragment.GetLength());
     for (uint32_t offset = aPoint.Offset(); offset; --offset) {
       if (textFragment.CharAt(offset - 1) == HTMLEditUtils::kNewLine) {
         return EditorDOMPointType(textNode, offset - 1);
       }
     }
     return EditorDOMPointType();
@@ -1860,20 +1865,21 @@ class HTMLEditUtils final {
    * points inclusive next preformatted linefeed if there is and aPoint is in a
    * text node. If the node's linefeed characters are not preformatted or aPoint
    * is not in a text node, this returns unset DOM point.
    */
   template <typename EditorDOMPointType, typename ArgEditorDOMPointType>
   static EditorDOMPointType GetInclusiveNextPreformattedNewLineInTextNode(
       const ArgEditorDOMPointType& aPoint) {
     if (!aPoint.IsInTextNode() || aPoint.IsEndOfContainer() ||
-        !EditorUtils::IsNewLinePreformatted(*aPoint.ContainerAsText())) {
+        !EditorUtils::IsNewLinePreformatted(
+            *aPoint.template ContainerAs<Text>())) {
       return EditorDOMPointType();
     }
-    Text* textNode = aPoint.ContainerAsText();
+    Text* textNode = aPoint.template ContainerAs<Text>();
     const nsTextFragment& textFragment = textNode->TextFragment();
     for (uint32_t offset = aPoint.Offset(); offset < textFragment.GetLength();
          ++offset) {
       if (textFragment.CharAt(offset) == HTMLEditUtils::kNewLine) {
         return EditorDOMPointType(textNode, offset);
       }
     }
     return EditorDOMPointType();
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -2021,18 +2021,18 @@ HTMLEditor::InsertNodeIntoProperAncestor
       return ResultType(NS_ERROR_FAILURE);
     }
 
     // Get the next point.
     pointToInsert = pointToInsert.ParentPoint();
 
     if (MOZ_UNLIKELY(
             !pointToInsert.IsInContentNode() ||
-            !EditorUtils::IsEditableContent(*pointToInsert.ContainerAsContent(),
-                                            EditorType::HTML))) {
+            !EditorUtils::IsEditableContent(
+                *pointToInsert.ContainerAs<nsIContent>(), EditorType::HTML))) {
       NS_WARNING(
           "There was no proper container element to insert the content node in "
           "the editing host");
       return ResultType(NS_ERROR_FAILURE);
     }
   }
 
   if (pointToInsert != aPointToInsert) {
@@ -2820,30 +2820,29 @@ Element* HTMLEditor::GetInclusiveAncesto
 
 Element* HTMLEditor::GetInclusiveAncestorByTagNameAtSelection(
     const nsStaticAtom& aTagName) const {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(&aTagName != nsGkAtoms::_empty);
 
   // If no node supplied, get it from anchor node of current selection
   const EditorRawDOMPoint atAnchor(SelectionRef().AnchorRef());
-  if (NS_WARN_IF(!atAnchor.IsSet()) ||
-      NS_WARN_IF(!atAnchor.GetContainerAsContent())) {
+  if (NS_WARN_IF(!atAnchor.IsInContentNode())) {
     return nullptr;
   }
 
   // Try to get the actual selected node
   nsIContent* content = nullptr;
   if (atAnchor.GetContainer()->HasChildNodes() &&
-      atAnchor.GetContainerAsContent()) {
+      atAnchor.ContainerAs<nsIContent>()) {
     content = atAnchor.GetChild();
   }
   // Anchor node is probably a text node - just use that
   if (!content) {
-    content = atAnchor.GetContainerAsContent();
+    content = atAnchor.ContainerAs<nsIContent>();
     if (NS_WARN_IF(!content)) {
       return nullptr;
     }
   }
   return GetInclusiveAncestorByTagNameInternal(aTagName, *content);
 }
 
 Element* HTMLEditor::GetInclusiveAncestorByTagNameInternal(
@@ -4554,17 +4553,17 @@ SplitNodeResult HTMLEditor::SplitNodeWit
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (MOZ_UNLIKELY(NS_WARN_IF(!aStartOfRightNode.IsInContentNode()))) {
     return SplitNodeResult(NS_ERROR_INVALID_ARG);
   }
   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
 
   if (MOZ_UNLIKELY(NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(
-          *aStartOfRightNode.ContainerAsContent())))) {
+          *aStartOfRightNode.ContainerAs<nsIContent>())))) {
     return SplitNodeResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
 
   IgnoredErrorResult ignoredError;
   AutoEditSubActionNotifier startToHandleEditSubAction(
       *this, EditSubAction::eSplitNode, nsIEditor::eNext, ignoredError);
   if (MOZ_UNLIKELY(
           NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED)))) {
@@ -4620,42 +4619,42 @@ SplitNodeResult HTMLEditor::SplitNodeDee
   SplitNodeResult lastResult =
       SplitNodeResult::NotHandled(atStartOfRightNode, GetSplitNodeDirection());
 
   while (true) {
     // Need to insert rules code call here to do things like not split a list
     // if you are after the last <li> or before the first, etc.  For now we
     // just have some smarts about unnecessarily splitting text nodes, which
     // should be universal enough to put straight in this EditorBase routine.
-    nsIContent* splittingContent = atStartOfRightNode.GetContainerAsContent();
+    auto* splittingContent = atStartOfRightNode.GetContainerAs<nsIContent>();
     if (NS_WARN_IF(!splittingContent)) {
       lastResult.IgnoreCaretPointSuggestion();
       return SplitNodeResult(NS_ERROR_FAILURE);
     }
     // If we meet an orphan node before meeting aMostAncestorToSplit, we need
     // to stop splitting.  This is a bug of the caller.
     if (NS_WARN_IF(splittingContent != &aMostAncestorToSplit &&
-                   !atStartOfRightNode.GetContainerParentAsContent())) {
+                   !atStartOfRightNode.GetContainerParentAs<nsIContent>())) {
       lastResult.IgnoreCaretPointSuggestion();
       return SplitNodeResult(NS_ERROR_FAILURE);
     }
     // If the container is not splitable node such as comment node, atomic
     // element, etc, we should keep it as-is, and try to split its parents.
     if (!HTMLEditUtils::IsSplittableNode(*splittingContent)) {
       if (splittingContent == &aMostAncestorToSplit) {
         return lastResult;
       }
       atStartOfRightNode.Set(splittingContent);
       continue;
     }
 
     // If the split point is middle of the node or the node is not a text node
     // and we're allowed to create empty element node, split it.
     if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
-         !atStartOfRightNode.GetContainerAsText()) ||
+         !atStartOfRightNode.IsInTextNode()) ||
         (!atStartOfRightNode.IsStartOfContainer() &&
          !atStartOfRightNode.IsEndOfContainer())) {
       lastResult = SplitNodeResult::MergeWithDeeperSplitNodeResult(
           SplitNodeWithTransaction(atStartOfRightNode), lastResult);
       if (lastResult.isErr()) {
         NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
         return lastResult;
       }
@@ -4756,17 +4755,17 @@ SplitNodeResult HTMLEditor::DoSplitNode(
     NS_WARNING("nsINode::InsertBefore() failed");
     return SplitNodeResult(error.StealNSResult());
   }
 
   // At this point, the existing right node has all the children.  Move all
   // the children which are before aStartOfRightNode.
   if (!aStartOfRightNode.IsStartOfContainer()) {
     // If it's a text node, just shuffle around some text
-    Text* rightAsText = aStartOfRightNode.GetContainerAsText();
+    Text* rightAsText = aStartOfRightNode.GetContainerAs<Text>();
     Text* leftAsText = aNewNode.GetAsText();
     if (rightAsText && leftAsText) {
       // Fix right node
       nsAutoString leftText;
       IgnoredErrorResult ignoredError;
       rightAsText->SubstringData(0, aStartOfRightNode.Offset(), leftText,
                                  ignoredError);
       NS_WARNING_ASSERTION(!ignoredError.Failed(),
@@ -4910,22 +4909,22 @@ SplitNodeResult HTMLEditor::DoSplitNode(
                      aStartOfRightNode.GetContainer()->GetParentNode()) ||
           NS_WARN_IF(parent != aNewNode.GetParentNode()) ||
           NS_WARN_IF(aNewNode.GetNextSibling() !=
                      aStartOfRightNode.GetContainer()))) {
     return SplitNodeResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
 
   DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjSplitNode(
-      *aStartOfRightNode.ContainerAsContent(), aStartOfRightNode.Offset(),
+      *aStartOfRightNode.ContainerAs<nsIContent>(), aStartOfRightNode.Offset(),
       aNewNode, GetSplitNodeDirection());
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                        "RangeUpdater::SelAdjSplitNode() failed, but ignored");
 
-  return SplitNodeResult(aNewNode, *aStartOfRightNode.ContainerAsContent(),
+  return SplitNodeResult(aNewNode, *aStartOfRightNode.ContainerAs<nsIContent>(),
                          GetSplitNodeDirection());
 }
 
 JoinNodesResult HTMLEditor::JoinNodesWithTransaction(
     nsIContent& aLeftContent, nsIContent& aRightContent) {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(&aLeftContent != &aRightContent);
   MOZ_ASSERT(aLeftContent.GetParentNode());
@@ -5686,17 +5685,17 @@ nsresult HTMLEditor::SetCSSBackgroundCol
 
       if (startOfRange.GetContainer() == endOfRange.GetContainer()) {
         // If the range is in a text node, set background color of its parent
         // block.
         if (startOfRange.IsInTextNode()) {
           if (const RefPtr<nsStyledElement> editableBlockStyledElement =
                   nsStyledElement::FromNodeOrNull(
                       HTMLEditUtils::GetAncestorElement(
-                          *startOfRange.ContainerAsText(),
+                          *startOfRange.ContainerAs<Text>(),
                           HTMLEditUtils::ClosestEditableBlockElement))) {
             Result<int32_t, nsresult> result =
                 mCSSEditUtils->SetCSSEquivalentToHTMLStyleWithTransaction(
                     *editableBlockStyledElement, nullptr, nsGkAtoms::bgcolor,
                     &aColor);
             if (result.isErr()) {
               if (result.inspectErr() == NS_ERROR_EDITOR_DESTROYED) {
                 NS_WARNING(
@@ -5714,17 +5713,17 @@ nsresult HTMLEditor::SetCSSBackgroundCol
 
         // If `Selection` is collapsed in a `<body>` element, set background
         // color of the `<body>` element.
         // XXX Why do we refer whether the `Selection` is collapsed rather
         //     than the `nsRange` is collapsed?
         if (startOfRange.GetContainer()->IsHTMLElement(nsGkAtoms::body) &&
             selectionIsCollapsed) {
           if (RefPtr<nsStyledElement> styledElement =
-                  startOfRange.GetContainerAsStyledElement()) {
+                  startOfRange.GetContainerAs<nsStyledElement>()) {
             Result<int32_t, nsresult> result =
                 mCSSEditUtils->SetCSSEquivalentToHTMLStyleWithTransaction(
                     *styledElement, nullptr, nsGkAtoms::bgcolor, &aColor);
             if (result.isErr()) {
               if (result.inspectErr() == NS_ERROR_EDITOR_DESTROYED) {
                 NS_WARNING(
                     "CSSEditUtils::SetCSSEquivalentToHTMLStyleWithTransaction("
                     "nsGkAtoms::bgcolor) destroyed the editor");
@@ -5794,20 +5793,20 @@ nsresult HTMLEditor::SetCSSBackgroundCol
       }
 
       // This caches block parent if we set its background color.
       RefPtr<Element> handledBlockParent;
 
       // If start node is a text node, set background color of its parent
       // block.
       if (startOfRange.IsInTextNode() &&
-          EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
+          EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
                                          EditorType::HTML)) {
         Element* const editableBlockElement = HTMLEditUtils::GetAncestorElement(
-            *startOfRange.ContainerAsText(),
+            *startOfRange.ContainerAs<Text>(),
             HTMLEditUtils::ClosestEditableBlockElement);
         if (editableBlockElement &&
             handledBlockParent != editableBlockElement) {
           handledBlockParent = editableBlockElement;
           if (nsStyledElement* blockStyledElement =
                   nsStyledElement::FromNode(handledBlockParent)) {
             // MOZ_KnownLive(*blockStyledElement): It's handledBlockParent
             // whose type is RefPtr.
@@ -5860,20 +5859,20 @@ nsresult HTMLEditor::SetCSSBackgroundCol
             }
           }
         }
       }
 
       // Finally, if end node is a text node, set background color of its
       // parent block.
       if (endOfRange.IsInTextNode() &&
-          EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
+          EditorUtils::IsEditableContent(*endOfRange.ContainerAs<Text>(),
                                          EditorType::HTML)) {
         Element* const editableBlockElement = HTMLEditUtils::GetAncestorElement(
-            *endOfRange.ContainerAsText(),
+            *endOfRange.ContainerAs<Text>(),
             HTMLEditUtils::ClosestEditableBlockElement);
         if (editableBlockElement &&
             handledBlockParent != editableBlockElement) {
           if (RefPtr<nsStyledElement> blockStyledElement =
                   nsStyledElement::FromNode(editableBlockElement)) {
             Result<int32_t, nsresult> result =
                 mCSSEditUtils->SetCSSEquivalentToHTMLStyleWithTransaction(
                     *blockStyledElement, nullptr, nsGkAtoms::bgcolor, &aColor);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -2107,17 +2107,18 @@ class HTMLEditor final : public EditorBa
       const EditorDOMPointType& aPoint) {
     MOZ_ASSERT(aPoint.IsInTextNode());
     if (aPoint.IsStartOfContainer()) {
       return CharPointType::TextEnd;
     }
     if (aPoint.IsPreviousCharPreformattedNewLine()) {
       return CharPointType::PreformattedLineBreak;
     }
-    if (EditorUtils::IsWhiteSpacePreformatted(*aPoint.ContainerAsText())) {
+    if (EditorUtils::IsWhiteSpacePreformatted(
+            *aPoint.template ContainerAs<Text>())) {
       return CharPointType::PreformattedChar;
     }
     if (aPoint.IsPreviousCharASCIISpace()) {
       return CharPointType::ASCIIWhiteSpace;
     }
     return aPoint.IsPreviousCharNBSP() ? CharPointType::NoBreakingSpace
                                        : CharPointType::VisibleChar;
   }
@@ -2125,17 +2126,18 @@ class HTMLEditor final : public EditorBa
   static CharPointType GetCharPointType(const EditorDOMPointType& aPoint) {
     MOZ_ASSERT(aPoint.IsInTextNode());
     if (aPoint.IsEndOfContainer()) {
       return CharPointType::TextEnd;
     }
     if (aPoint.IsCharPreformattedNewLine()) {
       return CharPointType::PreformattedLineBreak;
     }
-    if (EditorUtils::IsWhiteSpacePreformatted(*aPoint.ContainerAsText())) {
+    if (EditorUtils::IsWhiteSpacePreformatted(
+            *aPoint.template ContainerAs<Text>())) {
       return CharPointType::PreformattedChar;
     }
     if (aPoint.IsCharASCIISpace()) {
       return CharPointType::ASCIIWhiteSpace;
     }
     return aPoint.IsCharNBSP() ? CharPointType::NoBreakingSpace
                                : CharPointType::VisibleChar;
   }
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -737,17 +737,17 @@ nsresult HTMLEditor::HTMLWithContextInse
   }
 
   const bool insertionPointWasInLink =
       !!HTMLEditor::GetLinkElement(pointToInsert.GetContainer());
 
   if (pointToInsert.IsInTextNode()) {
     const SplitNodeResult splitNodeResult =
         mHTMLEditor.SplitNodeDeepWithTransaction(
-            MOZ_KnownLive(*pointToInsert.GetContainerAsContent()),
+            MOZ_KnownLive(*pointToInsert.ContainerAs<nsIContent>()),
             pointToInsert, SplitAtEdges::eAllowToCreateEmptyContainer);
     if (splitNodeResult.isErr()) {
       NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed");
       return splitNodeResult.unwrapErr();
     }
     nsresult rv = splitNodeResult.SuggestCaretPointTo(
         mHTMLEditor, {SuggestCaret::OnlyIfHasSuggestion,
                       SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
@@ -830,17 +830,17 @@ HTMLEditor::HTMLWithContextInserter::Ins
     nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents,
     const nsINode* aFragmentAsNode) {
   EditorDOMPoint pointToInsert{aPointToInsert};
 
   // Loop over the node list and paste the nodes:
   const RefPtr<const Element> maybeNonEditableBlockElement =
       pointToInsert.IsInContentNode()
           ? HTMLEditUtils::GetInclusiveAncestorElement(
-                *pointToInsert.ContainerAsContent(),
+                *pointToInsert.ContainerAs<nsIContent>(),
                 HTMLEditUtils::ClosestBlockElement)
           : nullptr;
 
   EditorDOMPoint lastInsertedPoint;
   nsCOMPtr<nsIContent> insertedContextParentContent;
   for (OwningNonNull<nsIContent>& content : aArrayOfTopMostChildContents) {
     if (NS_WARN_IF(content == aFragmentAsNode) ||
         NS_WARN_IF(content->IsHTMLElement(nsGkAtoms::body))) {
--- a/editor/libeditor/HTMLEditorDeleteHandler.cpp
+++ b/editor/libeditor/HTMLEditorDeleteHandler.cpp
@@ -1146,19 +1146,19 @@ EditActionResult HTMLEditor::HandleDelet
   // XXX At here, selection may have no range because of mutation event
   //     listeners can do anything so that we should just return NS_OK instead
   //     of returning error.
   const auto atNewStartOfSelection =
       GetFirstSelectionStartPoint<EditorDOMPoint>();
   if (NS_WARN_IF(!atNewStartOfSelection.IsSet())) {
     return EditActionHandled(NS_ERROR_FAILURE);
   }
-  if (atNewStartOfSelection.GetContainerAsContent()) {
+  if (atNewStartOfSelection.IsInContentNode()) {
     nsresult rv = DeleteMostAncestorMailCiteElementIfEmpty(
-        MOZ_KnownLive(*atNewStartOfSelection.GetContainerAsContent()));
+        MOZ_KnownLive(*atNewStartOfSelection.ContainerAs<nsIContent>()));
     if (NS_FAILED(rv)) {
       NS_WARNING(
           "HTMLEditor::DeleteMostAncestorMailCiteElementIfEmpty() failed");
       return EditActionHandled(rv);
     }
   }
   return EditActionHandled();
 }
@@ -1187,20 +1187,20 @@ nsresult HTMLEditor::AutoDeleteRangesHan
         aRangesToDelete.GetFirstRangeStartPoint<EditorDOMPoint>();
     if (NS_WARN_IF(!startPoint.IsSet())) {
       return NS_ERROR_FAILURE;
     }
     RefPtr<Element> editingHost = aHTMLEditor.ComputeEditingHost();
     if (NS_WARN_IF(!editingHost)) {
       return NS_ERROR_FAILURE;
     }
-    if (startPoint.GetContainerAsContent()) {
+    if (startPoint.IsInContentNode()) {
       AutoEmptyBlockAncestorDeleter deleter;
       if (deleter.ScanEmptyBlockInclusiveAncestor(
-              aHTMLEditor, *startPoint.GetContainerAsContent())) {
+              aHTMLEditor, *startPoint.ContainerAs<nsIContent>())) {
         nsresult rv = deleter.ComputeTargetRanges(
             aHTMLEditor, aDirectionAndAmount, *editingHost, aRangesToDelete);
         NS_WARNING_ASSERTION(
             NS_SUCCEEDED(rv),
             "AutoEmptyBlockAncestorDeleter::ComputeTargetRanges() failed");
         return rv;
       }
     }
@@ -1269,17 +1269,17 @@ nsresult HTMLEditor::AutoDeleteRangesHan
     }
 
     if (aRangesToDelete.IsCollapsed()) {
       const auto caretPoint =
           aRangesToDelete.GetFirstRangeStartPoint<EditorDOMPoint>();
       if (MOZ_UNLIKELY(NS_WARN_IF(!caretPoint.IsInContentNode()))) {
         return NS_ERROR_FAILURE;
       }
-      if (!EditorUtils::IsEditableContent(*caretPoint.ContainerAsContent(),
+      if (!EditorUtils::IsEditableContent(*caretPoint.ContainerAs<nsIContent>(),
                                           EditorType::HTML)) {
         return NS_SUCCESS_DOM_NO_OPERATION;
       }
       WSRunScanner wsRunScannerAtCaret(editingHost, caretPoint);
       WSScanResult scanFromCaretPointResult =
           aDirectionAndAmount == nsIEditor::eNext
               ? wsRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
                     caretPoint)
@@ -1439,23 +1439,23 @@ EditActionResult HTMLEditor::AutoDeleteR
   if (selectionWasCollapsed == SelectionWasCollapsed::Yes) {
     const auto startPoint =
         aRangesToDelete.GetFirstRangeStartPoint<EditorDOMPoint>();
     if (MOZ_UNLIKELY(NS_WARN_IF(!startPoint.IsSet()))) {
       return EditActionResult(NS_ERROR_FAILURE);
     }
 
     // If we are inside an empty block, delete it.
-    if (startPoint.GetContainerAsContent()) {
+    if (startPoint.IsInContentNode()) {
 #ifdef DEBUG
       nsMutationGuard debugMutation;
 #endif  // #ifdef DEBUG
       AutoEmptyBlockAncestorDeleter deleter;
       if (deleter.ScanEmptyBlockInclusiveAncestor(
-              aHTMLEditor, *startPoint.GetContainerAsContent())) {
+              aHTMLEditor, *startPoint.ContainerAs<nsIContent>())) {
         EditActionResult result = deleter.Run(aHTMLEditor, aDirectionAndAmount);
         if (result.Failed() || result.Handled()) {
           NS_WARNING_ASSERTION(result.Succeeded(),
                                "AutoEmptyBlockAncestorDeleter::Run() failed");
           return result;
         }
       }
       MOZ_ASSERT(!debugMutation.Mutated(0),
@@ -1550,17 +1550,17 @@ EditActionResult HTMLEditor::AutoDeleteR
       return result;
     }
 
     if (aRangesToDelete.IsCollapsed()) {
       // Use the original caret position for handling the deletion around
       // collapsed range because the container may be different from the
       // new collapsed position's container.
       if (!EditorUtils::IsEditableContent(
-              *caretPoint.ref().ContainerAsContent(), EditorType::HTML)) {
+              *caretPoint.ref().ContainerAs<nsIContent>(), EditorType::HTML)) {
         return EditActionCanceled();
       }
       WSRunScanner wsRunScannerAtCaret(&aEditingHost, caretPoint.ref());
       WSScanResult scanFromCaretPointResult =
           aDirectionAndAmount == nsIEditor::eNext
               ? wsRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
                     caretPoint.ref())
               : wsRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
@@ -1784,17 +1784,17 @@ HTMLEditor::AutoDeleteRangesHandler::Han
     const WSRunScanner& aWSRunScannerAtCaret,
     const WSScanResult& aScanFromCaretPointResult,
     const Element& aEditingHost) {
   MOZ_ASSERT(aHTMLEditor.IsTopLevelEditSubActionDataAvailable());
   MOZ_ASSERT(aRangesToDelete.IsCollapsed());
   MOZ_ASSERT(aDirectionAndAmount != nsIEditor::eNone);
   MOZ_ASSERT(aWSRunScannerAtCaret.ScanStartRef().IsInContentNode());
   MOZ_ASSERT(EditorUtils::IsEditableContent(
-      *aWSRunScannerAtCaret.ScanStartRef().ContainerAsContent(),
+      *aWSRunScannerAtCaret.ScanStartRef().ContainerAs<nsIContent>(),
       EditorType::HTML));
 
   if (StaticPrefs::editor_white_space_normalization_blink_compatible()) {
     if (aScanFromCaretPointResult.InCollapsibleWhiteSpaces() ||
         aScanFromCaretPointResult.InNonCollapsibleCharacters() ||
         aScanFromCaretPointResult.ReachedPreformattedLineBreak()) {
       nsresult rv = aRangesToDelete.Collapse(
           aScanFromCaretPointResult.Point<EditorRawDOMPoint>());
@@ -2073,17 +2073,17 @@ EditActionResult HTMLEditor::AutoDeleteR
     HandleDeleteCollapsedSelectionAtVisibleChar(
         HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount,
         const EditorDOMPoint& aPointToDelete) {
   MOZ_ASSERT(aHTMLEditor.IsTopLevelEditSubActionDataAvailable());
   MOZ_ASSERT(!StaticPrefs::editor_white_space_normalization_blink_compatible());
   MOZ_ASSERT(aPointToDelete.IsSet());
   MOZ_ASSERT(aPointToDelete.IsInTextNode());
 
-  OwningNonNull<Text> visibleTextNode = *aPointToDelete.GetContainerAsText();
+  OwningNonNull<Text> visibleTextNode = *aPointToDelete.ContainerAs<Text>();
   EditorDOMPoint startToDelete, endToDelete;
   if (aDirectionAndAmount == nsIEditor::ePrevious) {
     if (aPointToDelete.IsStartOfContainer()) {
       return EditActionResult(NS_ERROR_UNEXPECTED);
     }
     startToDelete = aPointToDelete.PreviousPoint();
     endToDelete = aPointToDelete;
     // Bug 1068979: delete both codepoints if surrogate pair
@@ -2120,18 +2120,18 @@ EditActionResult HTMLEditor::AutoDeleteR
           NS_EVENT_BITS_MUTATION_NODEREMOVED |
           NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
           NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
           NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED) &&
       (NS_WARN_IF(!startToDelete.IsSetAndValid()) ||
        NS_WARN_IF(!startToDelete.IsInTextNode()) ||
        NS_WARN_IF(!endToDelete.IsSetAndValid()) ||
        NS_WARN_IF(!endToDelete.IsInTextNode()) ||
-       NS_WARN_IF(startToDelete.ContainerAsText() != visibleTextNode) ||
-       NS_WARN_IF(endToDelete.ContainerAsText() != visibleTextNode) ||
+       NS_WARN_IF(startToDelete.ContainerAs<Text>() != visibleTextNode) ||
+       NS_WARN_IF(endToDelete.ContainerAs<Text>() != visibleTextNode) ||
        NS_WARN_IF(startToDelete.Offset() >= endToDelete.Offset()))) {
     NS_WARNING("Mutation event listener changed the DOM tree");
     return EditActionHandled(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
   rv = aHTMLEditor.DeleteTextWithTransaction(
       visibleTextNode, startToDelete.Offset(),
       endToDelete.Offset() - startToDelete.Offset());
   if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
@@ -2462,22 +2462,22 @@ bool HTMLEditor::AutoDeleteRangesHandler
   }
 
   // First find the adjacent node in the block
   if (aDirectionAndAmount == nsIEditor::ePrevious) {
     mLeafContentInOtherBlock = HTMLEditUtils::GetLastLeafContent(
         aOtherBlockElement, {LeafNodeType::OnlyEditableLeafNode},
         &aOtherBlockElement);
     mLeftContent = mLeafContentInOtherBlock;
-    mRightContent = aCaretPoint.GetContainerAsContent();
+    mRightContent = aCaretPoint.GetContainerAs<nsIContent>();
   } else {
     mLeafContentInOtherBlock = HTMLEditUtils::GetFirstLeafContent(
         aOtherBlockElement, {LeafNodeType::OnlyEditableLeafNode},
         &aOtherBlockElement);
-    mLeftContent = aCaretPoint.GetContainerAsContent();
+    mLeftContent = aCaretPoint.GetContainerAs<nsIContent>();
     mRightContent = mLeafContentInOtherBlock;
   }
 
   // Next to a block.  See if we are between the block and a `<br>`.
   // If so, we really want to delete the `<br>`.  Else join content at
   // selection to the block.
   WSScanResult scanFromCaretResult =
       aDirectionAndAmount == nsIEditor::eNext
@@ -2813,22 +2813,22 @@ bool HTMLEditor::AutoDeleteRangesHandler
   if (NS_WARN_IF(!editingHost)) {
     return false;
   }
 
   if (aDirectionAndAmount == nsIEditor::ePrevious) {
     mLeftContent = HTMLEditUtils::GetPreviousContent(
         aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
         editingHost);
-    mRightContent = aCaretPoint.GetContainerAsContent();
+    mRightContent = aCaretPoint.GetContainerAs<nsIContent>();
   } else {
     mRightContent = HTMLEditUtils::GetNextContent(
         aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
         editingHost);
-    mLeftContent = aCaretPoint.GetContainerAsContent();
+    mLeftContent = aCaretPoint.GetContainerAs<nsIContent>();
   }
 
   // Nothing to join
   if (!mLeftContent || !mRightContent) {
     return false;
   }
 
   // Don't cross table boundaries.
@@ -3529,31 +3529,31 @@ bool HTMLEditor::AutoDeleteRangesHandler
 }
 
 nsresult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
     DeleteTextAtStartAndEndOfRange(HTMLEditor& aHTMLEditor, nsRange& aRange) {
   EditorDOMPoint rangeStart(aRange.StartRef());
   EditorDOMPoint rangeEnd(aRange.EndRef());
   if (rangeStart.IsInTextNode() && !rangeStart.IsEndOfContainer()) {
     // Delete to last character
-    OwningNonNull<Text> textNode = *rangeStart.GetContainerAsText();
+    OwningNonNull<Text> textNode = *rangeStart.ContainerAs<Text>();
     nsresult rv = aHTMLEditor.DeleteTextWithTransaction(
         textNode, rangeStart.Offset(),
         rangeStart.GetContainer()->Length() - rangeStart.Offset());
     if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_FAILED(rv)) {
       NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
       return rv;
     }
   }
   if (rangeEnd.IsInTextNode() && !rangeEnd.IsStartOfContainer()) {
     // Delete to first character
-    OwningNonNull<Text> textNode = *rangeEnd.GetContainerAsText();
+    OwningNonNull<Text> textNode = *rangeEnd.ContainerAs<Text>();
     nsresult rv =
         aHTMLEditor.DeleteTextWithTransaction(textNode, 0, rangeEnd.Offset());
     if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_FAILED(rv)) {
       NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
       return rv;
@@ -3751,19 +3751,19 @@ EditActionResult HTMLEditor::AutoDeleteR
 
 nsresult
 HTMLEditor::AutoDeleteRangesHandler::DeleteUnnecessaryNodesAndCollapseSelection(
     HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount,
     const EditorDOMPoint& aSelectionStartPoint,
     const EditorDOMPoint& aSelectionEndPoint) {
   MOZ_ASSERT(aHTMLEditor.IsTopLevelEditSubActionDataAvailable());
   MOZ_ASSERT(EditorUtils::IsEditableContent(
-      *aSelectionStartPoint.ContainerAsContent(), EditorType::HTML));
+      *aSelectionStartPoint.ContainerAs<nsIContent>(), EditorType::HTML));
   MOZ_ASSERT(EditorUtils::IsEditableContent(
-      *aSelectionEndPoint.ContainerAsContent(), EditorType::HTML));
+      *aSelectionEndPoint.ContainerAs<nsIContent>(), EditorType::HTML));
 
   EditorDOMPoint atCaret(aSelectionStartPoint);
   EditorDOMPoint selectionEndPoint(aSelectionEndPoint);
 
   // If we're handling D&D, this is called to delete dragging item from the
   // tree.  In this case, we should remove parent blocks if it becomes empty.
   if (aHTMLEditor.GetEditAction() == EditAction::eDrop ||
       aHTMLEditor.GetEditAction() == EditAction::eDeleteByDrag) {
@@ -3796,46 +3796,47 @@ HTMLEditor::AutoDeleteRangesHandler::Del
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                            "EditorBase::CollapseSelectionTo() failed");
       return rv;
     }
   }
 
   if (NS_WARN_IF(!atCaret.IsInContentNode()) ||
       NS_WARN_IF(!selectionEndPoint.IsInContentNode()) ||
-      NS_WARN_IF(!EditorUtils::IsEditableContent(*atCaret.ContainerAsContent(),
-                                                 EditorType::HTML)) ||
       NS_WARN_IF(!EditorUtils::IsEditableContent(
-          *selectionEndPoint.ContainerAsContent(), EditorType::HTML))) {
+          *atCaret.ContainerAs<nsIContent>(), EditorType::HTML)) ||
+      NS_WARN_IF(!EditorUtils::IsEditableContent(
+          *selectionEndPoint.ContainerAs<nsIContent>(), EditorType::HTML))) {
     return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   }
 
   // We might have left only collapsed white-space in the start/end nodes
   {
     AutoTrackDOMPoint startTracker(aHTMLEditor.RangeUpdaterRef(), &atCaret);
     AutoTrackDOMPoint endTracker(aHTMLEditor.RangeUpdaterRef(),
                                  &selectionEndPoint);
 
     nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode(
-        aHTMLEditor, MOZ_KnownLive(*atCaret.ContainerAsContent()));
+        aHTMLEditor, MOZ_KnownLive(*atCaret.ContainerAs<nsIContent>()));
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "AutoDeleteRangesHandler::DeleteNodeIfInvisibleAndEditableTextNode() "
         "failed to remove start node, but ignored");
     // If we've not handled the selection end container, and it's still
     // editable, let's handle it.
-    if (atCaret.ContainerAsContent() !=
-            selectionEndPoint.ContainerAsContent() &&
-        EditorUtils::IsEditableContent(*selectionEndPoint.ContainerAsContent(),
-                                       EditorType::HTML)) {
+    if (atCaret.ContainerAs<nsIContent>() !=
+            selectionEndPoint.ContainerAs<nsIContent>() &&
+        EditorUtils::IsEditableContent(
+            *selectionEndPoint.ContainerAs<nsIContent>(), EditorType::HTML)) {
       nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode(
-          aHTMLEditor, MOZ_KnownLive(*selectionEndPoint.ContainerAsContent()));
+          aHTMLEditor,
+          MOZ_KnownLive(*selectionEndPoint.ContainerAs<nsIContent>()));
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(
           NS_SUCCEEDED(rv),
           "AutoDeleteRangesHandler::DeleteNodeIfInvisibleAndEditableTextNode() "
           "failed to remove end node, but ignored");
     }
@@ -3994,57 +3995,57 @@ HTMLEditor::AutoDeleteRangesHandler::Com
                  howToHandleCollapsedRange ==
                      EditorBase::HowToHandleCollapsedRange::Ignore)) {
     return NS_ERROR_FAILURE;
   }
 
   auto extendRangeToSelectCharacterForward =
       [](nsRange& aRange, const EditorRawDOMPointInText& aCaretPoint) -> void {
     const nsTextFragment& textFragment =
-        aCaretPoint.ContainerAsText()->TextFragment();
+        aCaretPoint.ContainerAs<Text>()->TextFragment();
     if (!textFragment.GetLength()) {
       return;
     }
     if (textFragment.IsHighSurrogateFollowedByLowSurrogateAt(
             aCaretPoint.Offset())) {
       DebugOnly<nsresult> rvIgnored = aRange.SetStartAndEnd(
-          aCaretPoint.ContainerAsText(), aCaretPoint.Offset(),
-          aCaretPoint.ContainerAsText(), aCaretPoint.Offset() + 2);
+          aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset(),
+          aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset() + 2);
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                            "nsRange::SetStartAndEnd() failed");
       return;
     }
     DebugOnly<nsresult> rvIgnored = aRange.SetStartAndEnd(
-        aCaretPoint.ContainerAsText(), aCaretPoint.Offset(),
-        aCaretPoint.ContainerAsText(), aCaretPoint.Offset() + 1);
+        aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset(),
+        aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset() + 1);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                          "nsRange::SetStartAndEnd() failed");
   };
   auto extendRangeToSelectCharacterBackward =
       [](nsRange& aRange, const EditorRawDOMPointInText& aCaretPoint) -> void {
     if (aCaretPoint.IsStartOfContainer()) {
       return;
     }
     const nsTextFragment& textFragment =
-        aCaretPoint.ContainerAsText()->TextFragment();
+        aCaretPoint.ContainerAs<Text>()->TextFragment();
     if (!textFragment.GetLength()) {
       return;
     }
     if (textFragment.IsLowSurrogateFollowingHighSurrogateAt(
             aCaretPoint.Offset() - 1)) {
       DebugOnly<nsresult> rvIgnored = aRange.SetStartAndEnd(
-          aCaretPoint.ContainerAsText(), aCaretPoint.Offset() - 2,
-          aCaretPoint.ContainerAsText(), aCaretPoint.Offset());
+          aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset() - 2,
+          aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset());
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                            "nsRange::SetStartAndEnd() failed");
       return;
     }
     DebugOnly<nsresult> rvIgnored = aRange.SetStartAndEnd(
-        aCaretPoint.ContainerAsText(), aCaretPoint.Offset() - 1,
-        aCaretPoint.ContainerAsText(), aCaretPoint.Offset());
+        aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset() - 1,
+        aCaretPoint.ContainerAs<Text>(), aCaretPoint.Offset());
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                          "nsRange::SetStartAndEnd() failed");
   };
 
   RefPtr<Element> editingHost = aHTMLEditor.ComputeEditingHost();
   for (OwningNonNull<nsRange>& range : aRangesToDelete.Ranges()) {
     // If it's not collapsed, `DeleteRangeTransaction::Create()` will be called
     // with it and `DeleteRangeTransaction` won't modify the range.
@@ -4105,22 +4106,22 @@ HTMLEditor::AutoDeleteRangesHandler::Com
           range, EditorRawDOMPointInText(nextEditableContent->AsText(), 0));
       continue;
     }
 
     if (caretPoint.IsInTextNode()) {
       if (howToHandleCollapsedRange ==
           EditorBase::HowToHandleCollapsedRange::ExtendBackward) {
         extendRangeToSelectCharacterBackward(
-            range, EditorRawDOMPointInText(caretPoint.ContainerAsText(),
+            range, EditorRawDOMPointInText(caretPoint.ContainerAs<Text>(),
                                            caretPoint.Offset()));
         continue;
       }
       extendRangeToSelectCharacterForward(
-          range, EditorRawDOMPointInText(caretPoint.ContainerAsText(),
+          range, EditorRawDOMPointInText(caretPoint.ContainerAs<Text>(),
                                          caretPoint.Offset()));
       continue;
     }
 
     nsIContent* editableContent =
         howToHandleCollapsedRange ==
                 EditorBase::HowToHandleCollapsedRange::ExtendBackward
             ? HTMLEditUtils::GetPreviousContent(
@@ -4206,22 +4207,22 @@ nsresult HTMLEditor::DeleteTextAndTextNo
   };
 
   if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
       aStartPoint.IsInTextNode()) {
     if (aTreatEmptyTextNodes !=
             TreatEmptyTextNodes::KeepIfContainerOfRangeBoundaries &&
         aStartPoint.IsStartOfContainer() && aEndPoint.IsEndOfContainer()) {
       nsresult rv = deleteEmptyContentNodeWithTransaction(
-          MOZ_KnownLive(*aStartPoint.ContainerAsText()));
+          MOZ_KnownLive(*aStartPoint.template ContainerAs<Text>()));
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                            "deleteEmptyContentNodeWithTransaction() failed");
       return rv;
     }
-    RefPtr<Text> textNode = aStartPoint.ContainerAsText();
+    RefPtr<Text> textNode = aStartPoint.template ContainerAs<Text>();
     nsresult rv =
         DeleteTextWithTransaction(*textNode, aStartPoint.Offset(),
                                   aEndPoint.Offset() - aStartPoint.Offset());
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditor::DeleteTextWithTransaction() failed");
     return rv;
   }
 
@@ -4249,17 +4250,17 @@ nsresult HTMLEditor::DeleteTextAndTextNo
     if (textNode == aStartPoint.GetContainer()) {
       if (aStartPoint.IsEndOfContainer()) {
         continue;
       }
       if (aStartPoint.IsStartOfContainer() &&
           aTreatEmptyTextNodes !=
               TreatEmptyTextNodes::KeepIfContainerOfRangeBoundaries) {
         nsresult rv = deleteEmptyContentNodeWithTransaction(
-            MOZ_KnownLive(*aStartPoint.ContainerAsText()));
+            MOZ_KnownLive(*aStartPoint.template ContainerAs<Text>()));
         if (NS_FAILED(rv)) {
           NS_WARNING("deleteEmptyContentNodeWithTransaction() failed");
           return rv;
         }
         continue;
       }
       nsresult rv = DeleteTextWithTransaction(
           MOZ_KnownLive(textNode), aStartPoint.Offset(),
@@ -4277,17 +4278,17 @@ nsresult HTMLEditor::DeleteTextAndTextNo
     if (textNode == aEndPoint.GetContainer()) {
       if (aEndPoint.IsStartOfContainer()) {
         break;
       }
       if (aEndPoint.IsEndOfContainer() &&
           aTreatEmptyTextNodes !=
               TreatEmptyTextNodes::KeepIfContainerOfRangeBoundaries) {
         nsresult rv = deleteEmptyContentNodeWithTransaction(
-            MOZ_KnownLive(*aEndPoint.ContainerAsText()));
+            MOZ_KnownLive(*aEndPoint.template ContainerAs<Text>()));
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                              "deleteEmptyContentNodeWithTransaction() failed");
         return rv;
       }
       nsresult rv = DeleteTextWithTransaction(MOZ_KnownLive(textNode), 0,
                                               aEndPoint.Offset());
       if (NS_WARN_IF(Destroyed())) {
         return NS_ERROR_EDITOR_DESTROYED;
@@ -4582,17 +4583,17 @@ nsresult HTMLEditor::AutoDeleteRangesHan
       // may need to shrink the range.
       Element* editingHost = aHTMLEditor.ComputeEditingHost();
       NS_WARNING_ASSERTION(editingHost, "There was no editing host");
       nsIContent* nextContent =
           atStart.IsEndOfContainer() && range.StartRef().GetChild() &&
                   HTMLEditUtils::IsInvisibleBRElement(
                       *range.StartRef().GetChild())
               ? HTMLEditUtils::GetNextContent(
-                    *atStart.ContainerAsContent(),
+                    *atStart.ContainerAs<nsIContent>(),
                     {WalkTreeOption::IgnoreDataNodeExceptText,
                      WalkTreeOption::StopAtBlockBoundary},
                     editingHost)
               : nullptr;
       if (!nextContent || nextContent != range.StartRef().GetChild()) {
         noNeedToChangeStart = true;
         range.SetStart(
             aRangesToDelete.GetFirstRangeStartPoint<EditorDOMPoint>());
@@ -4735,32 +4736,32 @@ Result<bool, nsresult> HTMLEditor::CanMo
     return true;
   }
   EditorRawDOMPoint endPoint(oneLineRange->EndRef());
   if (!endPoint.IsStartOfContainer()) {
     return true;
   }
   if (startPoint.GetContainer() != commonAncestor) {
     while (true) {
-      EditorRawDOMPoint pointInParent(startPoint.GetContainerAsContent());
+      EditorRawDOMPoint pointInParent(startPoint.GetContainerAs<nsIContent>());
       if (NS_WARN_IF(!pointInParent.IsInContentNode())) {
         return Err(NS_ERROR_FAILURE);
       }
       if (pointInParent.GetContainer() == commonAncestor) {
         startPoint = pointInParent;
         break;
       }
       if (!pointInParent.IsEndOfContainer()) {
         return true;
       }
     }
   }
   if (endPoint.GetContainer() != commonAncestor) {
     while (true) {
-      EditorRawDOMPoint pointInParent(endPoint.GetContainerAsContent());
+      EditorRawDOMPoint pointInParent(endPoint.GetContainerAs<nsIContent>());
       if (NS_WARN_IF(!pointInParent.IsInContentNode())) {
         return Err(NS_ERROR_FAILURE);
       }
       if (pointInParent.GetContainer() == commonAncestor) {
         endPoint = pointInParent;
         break;
       }
       if (!pointInParent.IsStartOfContainer()) {
@@ -5051,17 +5052,17 @@ void HTMLEditor::MoveChildrenBetween(nsI
     }
   }
 
   if (NS_WARN_IF(children.LastElement() != &aLastChild)) {
     aError.Throw(NS_ERROR_INVALID_ARG);
     return;
   }
 
-  nsCOMPtr<nsIContent> newContainer = aPointToInsert.ContainerAsContent();
+  nsCOMPtr<nsIContent> newContainer = aPointToInsert.ContainerAs<nsIContent>();
   nsCOMPtr<nsIContent> nextNode = aPointToInsert.GetChild();
   for (size_t i = children.Length(); i > 0; --i) {
     nsCOMPtr<nsIContent>& child = children[i - 1];
     if (child->GetParentNode() != oldContainer) {
       // If the child has been moved to different container, we shouldn't
       // touch it.
       continue;
     }
@@ -5553,19 +5554,20 @@ HTMLEditor::AutoDeleteRangesHandler::Ext
           HTMLEditUtils::GetElementIfOnlyOneSelected(aRangeToDelete)) {
     if (HTMLEditUtils::IsAnyListElement(maybeListElement) &&
         !HTMLEditUtils::IsEmptyNode(*maybeListElement)) {
       EditorRawDOMRange range =
           HTMLEditUtils::GetRangeSelectingAllContentInAllListItems<
               EditorRawDOMRange>(*maybeListElement);
       if (range.IsPositioned()) {
         if (EditorUtils::IsEditableContent(
-                *range.StartRef().ContainerAsContent(), EditorType::HTML) &&
-            EditorUtils::IsEditableContent(*range.EndRef().ContainerAsContent(),
-                                           EditorType::HTML)) {
+                *range.StartRef().ContainerAs<nsIContent>(),
+                EditorType::HTML) &&
+            EditorUtils::IsEditableContent(
+                *range.EndRef().ContainerAs<nsIContent>(), EditorType::HTML)) {
           return range;
         }
       }
       // If the first and/or last list item is not editable, we need to do more
       // complicated things probably, but we just delete the list element with
       // invisible things around it for now since it must be rare case.
     }
     // Otherwise, if the list item is empty, we should delete it with invisible
@@ -5671,44 +5673,44 @@ HTMLEditor::AutoDeleteRangesHandler::Ext
   // all list items in it and it's not empty, we should make it have only one
   // list item which is empty.
   Element* selectedListElement =
       HTMLEditUtils::GetElementIfOnlyOneSelected(rangeToDelete);
   if (!selectedListElement ||
       !HTMLEditUtils::IsAnyListElement(selectedListElement)) {
     if (rangeToDelete.IsInContentNodes() && rangeToDelete.InSameContainer() &&
         HTMLEditUtils::IsAnyListElement(
-            rangeToDelete.StartRef().ContainerAsContent()) &&
+            rangeToDelete.StartRef().ContainerAs<nsIContent>()) &&
         rangeToDelete.StartRef().IsStartOfContainer() &&
         rangeToDelete.EndRef().IsEndOfContainer()) {
-      selectedListElement = rangeToDelete.StartRef().ContainerAsElement();
+      selectedListElement = rangeToDelete.StartRef().ContainerAs<Element>();
     } else {
       selectedListElement = nullptr;
     }
   }
   if (selectedListElement &&
       !HTMLEditUtils::IsEmptyNode(*selectedListElement)) {
     EditorRawDOMRange range =
         HTMLEditUtils::GetRangeSelectingAllContentInAllListItems<
             EditorRawDOMRange>(*selectedListElement);
     if (range.IsPositioned()) {
-      if (EditorUtils::IsEditableContent(*range.StartRef().ContainerAsContent(),
-                                         EditorType::HTML) &&
-          EditorUtils::IsEditableContent(*range.EndRef().ContainerAsContent(),
-                                         EditorType::HTML)) {
+      if (EditorUtils::IsEditableContent(
+              *range.StartRef().ContainerAs<nsIContent>(), EditorType::HTML) &&
+          EditorUtils::IsEditableContent(
+              *range.EndRef().ContainerAs<nsIContent>(), EditorType::HTML)) {
         return range;
       }
     }
   }
 
   if (atFirstInvisibleBRElement.IsInContentNode()) {
     // Find block node containing invisible `<br>` element.
     if (const RefPtr<const Element> editableBlockContainingBRElement =
             HTMLEditUtils::GetInclusiveAncestorElement(
-                *atFirstInvisibleBRElement.ContainerAsContent(),
+                *atFirstInvisibleBRElement.ContainerAs<nsIContent>(),
                 HTMLEditUtils::ClosestEditableBlockElement)) {
       if (rangeToDelete.Contains(
               EditorRawDOMPoint(editableBlockContainingBRElement))) {
         return rangeToDelete;
       }
       // Otherwise, the new range should end at the invisible `<br>`.
       if (aFrameSelection && !aFrameSelection->IsValidSelectionPoint(
                                  atFirstInvisibleBRElement.GetContainer())) {
--- a/editor/libeditor/HTMLEditorState.cpp
+++ b/editor/libeditor/HTMLEditorState.cpp
@@ -261,17 +261,17 @@ AlignStateAtSelection::AlignStateAtSelec
     return;
   }
   MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
 
   nsIContent* editTargetContent = nullptr;
   // If selection is collapsed or in a text node, take the container.
   if (aHTMLEditor.SelectionRef().IsCollapsed() ||
       atStartOfSelection.IsInTextNode()) {
-    editTargetContent = atStartOfSelection.GetContainerAsContent();
+    editTargetContent = atStartOfSelection.GetContainerAs<nsIContent>();
     if (NS_WARN_IF(!editTargetContent)) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
   }
   // If selection container is the `<body>` element which is set to
   // `HTMLDocument.body`, take first editable node in it.
   // XXX Why don't we just compare `atStartOfSelection.GetChild()` and
@@ -508,26 +508,21 @@ ParagraphStateAtSelection::ParagraphStat
     }
   }
 
   // We might have an empty node list.  if so, find selection parent
   // and put that on the list
   if (arrayOfContents.IsEmpty()) {
     const auto atCaret =
         aHTMLEditor.GetFirstSelectionStartPoint<EditorRawDOMPoint>();
-    if (NS_WARN_IF(!atCaret.IsSet())) {
+    if (NS_WARN_IF(!atCaret.IsInContentNode())) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
-    nsIContent* content = atCaret.GetContainerAsContent();
-    if (NS_WARN_IF(!content)) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-    arrayOfContents.AppendElement(*content);
+    arrayOfContents.AppendElement(*atCaret.ContainerAs<nsIContent>());
   }
 
   dom::Element* bodyOrDocumentElement = aHTMLEditor.GetRoot();
   if (NS_WARN_IF(!bodyOrDocumentElement)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -269,17 +269,17 @@ nsresult HTMLEditor::SetInlinePropertyIn
           NS_WARN_IF(!endOfRange.IsSet())) {
         continue;
       }
 
       // If range is in a text node, apply new style simply.
       if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
           startOfRange.IsInTextNode()) {
         nsresult rv = SetInlinePropertyOnTextNode(
-            MOZ_KnownLive(*startOfRange.GetContainerAsText()),
+            MOZ_KnownLive(*startOfRange.ContainerAs<Text>()),
             startOfRange.Offset(), endOfRange.Offset(), aProperty, aAttribute,
             aAttributeValue);
         if (NS_FAILED(rv)) {
           NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
           return rv;
         }
         continue;
       }
@@ -300,20 +300,20 @@ nsresult HTMLEditor::SetInlinePropertyIn
                                        *node->AsContent(), EditorType::HTML)) {
             arrayOfContents.AppendElement(*node->AsContent());
           }
         }
       }
 
       // If start node is a text node, apply new style to a part of it.
       if (startOfRange.IsInTextNode() &&
-          EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
+          EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
                                          EditorType::HTML)) {
         nsresult rv = SetInlinePropertyOnTextNode(
-            MOZ_KnownLive(*startOfRange.GetContainerAsText()),
+            MOZ_KnownLive(*startOfRange.ContainerAs<Text>()),
             startOfRange.Offset(), startOfRange.GetContainer()->Length(),
             aProperty, aAttribute, aAttributeValue);
         if (NS_FAILED(rv)) {
           NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
           return rv;
         }
       }
 
@@ -329,20 +329,20 @@ nsresult HTMLEditor::SetInlinePropertyIn
           return setStyleResult.unwrapErr();
         }
         // There is AutoTransactionsConserveSelection, so we don't need to
         // update selection here.
       }
 
       // Finally, if end node is a text node, apply new style to a part of it.
       if (endOfRange.IsInTextNode() &&
-          EditorUtils::IsEditableContent(*endOfRange.GetContainerAsText(),
+          EditorUtils::IsEditableContent(*endOfRange.ContainerAs<Text>(),
                                          EditorType::HTML)) {
         nsresult rv = SetInlinePropertyOnTextNode(
-            MOZ_KnownLive(*endOfRange.GetContainerAsText()), 0,
+            MOZ_KnownLive(*endOfRange.ContainerAs<Text>()), 0,
             endOfRange.Offset(), aProperty, aAttribute, aAttributeValue);
         if (NS_FAILED(rv)) {
           NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
           return rv;
         }
       }
     }
   }
@@ -974,18 +974,17 @@ SplitRangeOffResult HTMLEditor::SplitAnc
 
   return SplitRangeOffResult(std::move(startOfRange), std::move(resultAtStart),
                              std::move(endOfRange), std::move(resultAtEnd));
 }
 
 SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
     const EditorDOMPoint& aPointToSplit, nsAtom* aProperty,
     nsAtom* aAttribute) {
-  if (NS_WARN_IF(!aPointToSplit.IsSet()) ||
-      NS_WARN_IF(!aPointToSplit.GetContainerAsContent())) {
+  if (NS_WARN_IF(!aPointToSplit.IsInContentNode())) {
     return SplitNodeResult(NS_ERROR_INVALID_ARG);
   }
 
   // We assume that this method is called only when we're removing style(s).
   // Even if we're in HTML mode and there is no presentation element in the
   // block, we may need to overwrite the block's style with `<span>` element
   // and CSS.  For example, `<h1>` element has `font-weight: bold;` as its
   // default style.  If `Document.execCommand("bold")` is called for its
@@ -1167,20 +1166,20 @@ EditResult HTMLEditor::ClearStyleAt(cons
                                        ? firstLeafChildOfNextNode
                                        : splitResult.GetNextContent(),
                                    0);
   RefPtr<HTMLBRElement> brElement;
   // But don't try to split non-containers like `<br>`, `<hr>` and `<img>`
   // element.
   if (!atStartOfNextNode.IsInContentNode() ||
       !HTMLEditUtils::IsContainerNode(
-          *atStartOfNextNode.ContainerAsContent())) {
+          *atStartOfNextNode.ContainerAs<nsIContent>())) {
     // If it's a `<br>` element, let's move it into new node later.
     brElement = HTMLBRElement::FromNode(atStartOfNextNode.GetContainer());
-    if (!atStartOfNextNode.GetContainerParentAsContent()) {
+    if (!atStartOfNextNode.GetContainerParentAs<nsIContent>()) {
       NS_WARNING("atStartOfNextNode was in an orphan node");
       return EditResult(NS_ERROR_FAILURE);
     }
     atStartOfNextNode.Set(atStartOfNextNode.GetContainerParent(), 0);
   }
   const SplitNodeResult splitResultAtStartOfNextNode =
       SplitAncestorStyledInlineElementsAt(atStartOfNextNode, aProperty,
                                           aAttribute);
@@ -1595,17 +1594,17 @@ nsresult HTMLEditor::PromoteRangeIfStart
     }
     if (!HTMLEditUtils::IsNamedAnchor(element)) {
       continue;
     }
     newRangeStart.Set(element);
     break;
   }
 
-  if (!newRangeStart.GetContainerAsContent()) {
+  if (!newRangeStart.IsInContentNode()) {
     NS_WARNING(
         "HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor() reached root "
         "element from start container");
     return NS_ERROR_FAILURE;
   }
 
   EditorRawDOMPoint newRangeEnd(aRange.EndRef());
   for (Element* element :
@@ -1615,17 +1614,17 @@ nsresult HTMLEditor::PromoteRangeIfStart
     }
     if (!HTMLEditUtils::IsNamedAnchor(element)) {
       continue;
     }
     newRangeEnd.SetAfter(element);
     break;
   }
 
-  if (!newRangeEnd.GetContainerAsContent()) {
+  if (!newRangeEnd.IsInContentNode()) {
     NS_WARNING(
         "HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor() reached root "
         "element from end container");
     return NS_ERROR_FAILURE;
   }
 
   if (newRangeStart == aRange.StartRef() && newRangeEnd == aRange.EndRef()) {
     return NS_OK;
@@ -1648,17 +1647,17 @@ nsresult HTMLEditor::PromoteInlineRange(
     MOZ_ASSERT(newRangeStart.GetContainer() == content);
     if (content->IsHTMLElement(nsGkAtoms::body) ||
         !EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
         !IsStartOfContainerOrBeforeFirstEditableChild(newRangeStart)) {
       break;
     }
     newRangeStart.Set(content);
   }
-  if (!newRangeStart.GetContainerAsContent()) {
+  if (!newRangeStart.IsInContentNode()) {
     NS_WARNING(
         "HTMLEditor::PromoteInlineRange() reached root element from start "
         "container");
     return NS_ERROR_FAILURE;
   }
 
   EditorRawDOMPoint newRangeEnd(aRange.EndRef());
   for (nsIContent* content :
@@ -1666,17 +1665,17 @@ nsresult HTMLEditor::PromoteInlineRange(
     MOZ_ASSERT(newRangeEnd.GetContainer() == content);
     if (content->IsHTMLElement(nsGkAtoms::body) ||
         !EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
         !IsEndOfContainerOrEqualsOrAfterLastEditableChild(newRangeEnd)) {
       break;
     }
     newRangeEnd.SetAfter(content);
   }
-  if (!newRangeEnd.GetContainerAsContent()) {
+  if (!newRangeEnd.IsInContentNode()) {
     NS_WARNING(
         "HTMLEditor::PromoteInlineRange() reached root element from end "
         "container");
     return NS_ERROR_FAILURE;
   }
 
   if (newRangeStart == aRange.StartRef() && newRangeEnd == aRange.EndRef()) {
     return NS_OK;
@@ -2257,42 +2256,42 @@ nsresult HTMLEditor::RemoveInlinePropert
           NS_WARNING("nsRange::SetStartAndEnd() failed");
           return rv;
         }
 
         // Collect editable nodes which are entirely contained in the range.
         AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
         if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
             startOfRange.IsInTextNode()) {
-          if (!EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
+          if (!EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
                                               EditorType::HTML)) {
             continue;
           }
-          arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
+          arrayOfContents.AppendElement(*startOfRange.ContainerAs<Text>());
         } else if (startOfRange.IsInTextNode() && endOfRange.IsInTextNode() &&
                    startOfRange.GetContainer()->GetNextSibling() ==
                        endOfRange.GetContainer()) {
-          if (EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
+          if (EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
                                              EditorType::HTML)) {
-            arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
+            arrayOfContents.AppendElement(*startOfRange.ContainerAs<Text>());
           }
-          if (EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
+          if (EditorUtils::IsEditableContent(*endOfRange.ContainerAs<Text>(),
                                              EditorType::HTML)) {
-            arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
+            arrayOfContents.AppendElement(*endOfRange.ContainerAs<Text>());
           }
           if (arrayOfContents.IsEmpty()) {
             continue;
           }
         } else {
           // Append first node if it's a text node but selected not entirely.
           if (startOfRange.IsInTextNode() &&
               !startOfRange.IsStartOfContainer() &&
-              EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
+              EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
                                              EditorType::HTML)) {
-            arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
+            arrayOfContents.AppendElement(*startOfRange.ContainerAs<Text>());
           }
           // Append all entirely selected nodes.
           ContentSubtreeIterator subtreeIter;
           if (NS_SUCCEEDED(subtreeIter.Init(range))) {
             for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
               nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
               if (NS_WARN_IF(!node)) {
                 return NS_ERROR_FAILURE;
@@ -2302,19 +2301,19 @@ nsresult HTMLEditor::RemoveInlinePropert
                                                  EditorType::HTML)) {
                 arrayOfContents.AppendElement(*node->AsContent());
               }
             }
           }
           // Append last node if it's a text node but selected not entirely.
           if (startOfRange.GetContainer() != endOfRange.GetContainer() &&
               endOfRange.IsInTextNode() && !endOfRange.IsEndOfContainer() &&
-              EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
+              EditorUtils::IsEditableContent(*endOfRange.ContainerAs<Text>(),
                                              EditorType::HTML)) {
-            arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
+            arrayOfContents.AppendElement(*endOfRange.ContainerAs<Text>());
           }
         }
 
         for (OwningNonNull<nsIContent>& content : arrayOfContents) {
           if (content->IsElement()) {
             Result<EditorDOMPoint, nsresult> removeStyleResult =
                 RemoveStyleInside(MOZ_KnownLive(*content->AsElement()),
                                   MOZ_KnownLive(style.mProperty),
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -418,20 +418,20 @@ CreateElementResult HTMLEditor::InsertTa
          IntegerRange<uint32_t>(aNumberOfCellsToInsert)) {
       RefPtr<Element> newCell = CreateElementWithDefaults(*nsGkAtoms::td);
       if (!newCell) {
         NS_WARNING(
             "HTMLEditor::CreateElementWithDefaults(nsGkAtoms::td) failed");
         return NS_ERROR_FAILURE;
       }
       CreateElementResult insertNewCellResult = InsertNodeWithTransaction(
-          *newCell,
-          referenceContent
-              ? EditorDOMPoint(referenceContent)
-              : EditorDOMPoint::AtEndOf(*aPointToInsert.ContainerAsElement()));
+          *newCell, referenceContent
+                        ? EditorDOMPoint(referenceContent)
+                        : EditorDOMPoint::AtEndOf(
+                              *aPointToInsert.ContainerAs<Element>()));
       if (insertNewCellResult.isErr()) {
         NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
         return insertNewCellResult.unwrapErr();
       }
       lastCellElement = insertNewCellResult.UnwrapNewNode();
       if (!firstCellElement) {
         firstCellElement = lastCellElement;
       }
@@ -637,17 +637,17 @@ nsresult HTMLEditor::InsertTableColumnsW
 
   if (!HTMLEditUtils::IsTableRow(aPointToInsert.GetContainer())) {
     NS_WARNING("Tried to insert columns to non-<tr> element");
     return NS_ERROR_FAILURE;
   }
 
   const RefPtr<Element> tableElement =
       HTMLEditUtils::GetClosestAncestorTableElement(
-          *aPointToInsert.ContainerAsElement());
+          *aPointToInsert.ContainerAs<Element>());
   if (!tableElement) {
     NS_WARNING("There was no ancestor <table> element");
     return NS_ERROR_FAILURE;
   }
 
   const Result<TableSize, nsresult> tableSizeOrError =
       TableSize::Create(*this, *tableElement);
   if (NS_WARN_IF(tableSizeOrError.isErr())) {
@@ -665,17 +665,17 @@ nsresult HTMLEditor::InsertTableColumnsW
     if (!aPointToInsert.IsEndOfContainer() &&
         HTMLEditUtils::IsTableCell(aPointToInsert.GetChild())) {
       return false;  // Insert before the cell element.
     }
     // There is a previous cell element, we should add a column after it.
     Element* previousCellElement =
         aPointToInsert.IsEndOfContainer()
             ? HTMLEditUtils::GetLastTableCellElementChild(
-                  *aPointToInsert.ContainerAsElement())
+                  *aPointToInsert.ContainerAs<Element>())
             : HTMLEditUtils::GetPreviousTableCellElementSibling(
                   *aPointToInsert.GetChild());
     return previousCellElement != nullptr;
   }();
 
   // Consider the column index in the table from given point and direction.
   auto referenceColumnIndexOrError =
       [&]() MOZ_CAN_RUN_SCRIPT -> Result<int32_t, nsresult> {
@@ -693,17 +693,17 @@ nsresult HTMLEditor::InsertTableColumnsW
       }
       return cellIndexes.mColumn;
     }
 
     // Otherwise, insert columns immediately after the previous column.
     Element* previousCellElement =
         aPointToInsert.IsEndOfContainer()
             ? HTMLEditUtils::GetLastTableCellElementChild(
-                  *aPointToInsert.ContainerAsElement())
+                  *aPointToInsert.ContainerAs<Element>())
             : HTMLEditUtils::GetPreviousTableCellElementSibling(
                   *aPointToInsert.GetChild());
     MOZ_ASSERT(previousCellElement);
     CellIndexes cellIndexes(*previousCellElement, presShell);
     if (NS_WARN_IF(cellIndexes.isErr())) {
       return Err(NS_ERROR_FAILURE);
     }
     return cellIndexes.mColumn;
@@ -817,18 +817,18 @@ nsresult HTMLEditor::InsertTableColumnsW
                                           aNumberOfColumnsToInsert);
       if (insertCellElementsResult.isErr()) {
         NS_WARNING("HTMLEditor::InsertTableCellsWithTransaction() failed");
         return Err(insertCellElementsResult.unwrapErr());
       }
       // We'll update selection later into the first inserted cell element in
       // the current row.
       insertCellElementsResult.IgnoreCaretPointSuggestion();
-      if (pointToInsert.ContainerAsElement() ==
-          aPointToInsert.ContainerAsElement()) {
+      if (pointToInsert.ContainerAs<Element>() ==
+          aPointToInsert.ContainerAs<Element>()) {
         cellElementToPutCaret = insertCellElementsResult.UnwrapNewNode();
         MOZ_ASSERT(cellElementToPutCaret);
         MOZ_ASSERT(HTMLEditUtils::IsTableCell(cellElementToPutCaret));
       }
     }
     return cellElementToPutCaret;
   }();
   if (MOZ_UNLIKELY(cellElementToPutCaretOrError.isErr())) {
--- a/editor/libeditor/InsertTextTransaction.cpp
+++ b/editor/libeditor/InsertTextTransaction.cpp
@@ -28,17 +28,17 @@ already_AddRefed<InsertTextTransaction> 
   RefPtr<InsertTextTransaction> transaction =
       new InsertTextTransaction(aEditorBase, aStringToInsert, aPointToInsert);
   return transaction.forget();
 }
 
 InsertTextTransaction::InsertTextTransaction(
     EditorBase& aEditorBase, const nsAString& aStringToInsert,
     const EditorDOMPointInText& aPointToInsert)
-    : mTextNode(aPointToInsert.ContainerAsText()),
+    : mTextNode(aPointToInsert.ContainerAs<Text>()),
       mOffset(aPointToInsert.Offset()),
       mStringToInsert(aStringToInsert),
       mEditorBase(&aEditorBase) {}
 
 std::ostream& operator<<(std::ostream& aStream,
                          const InsertTextTransaction& aTransaction) {
   aStream << "{ mTextNode=" << aTransaction.mTextNode.get();
   if (aTransaction.mTextNode) {
--- a/editor/libeditor/SplitNodeTransaction.cpp
+++ b/editor/libeditor/SplitNodeTransaction.cpp
@@ -37,25 +37,25 @@ already_AddRefed<SplitNodeTransaction> S
   return transaction.forget();
 }
 
 template <typename PT, typename CT>
 SplitNodeTransaction::SplitNodeTransaction(
     HTMLEditor& aHTMLEditor,
     const EditorDOMPointBase<PT, CT>& aStartOfRightContent)
     : mHTMLEditor(&aHTMLEditor),
-      mSplitContent(aStartOfRightContent.GetContainerAsContent()),
+      mSplitContent(aStartOfRightContent.template GetContainerAs<nsIContent>()),
       mSplitOffset(aStartOfRightContent.Offset()) {
   // printf("SplitNodeTransaction size: %zu\n", sizeof(SplitNodeTransaction));
   static_assert(sizeof(SplitNodeTransaction) <= 64,
                 "Transaction classes may be created a lot and may be alive "
                 "long so that keep the foot print smaller as far as possible");
   MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.IsInContentNode());
   MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsSplittableNode(
-      *aStartOfRightContent.ContainerAsContent()));
+      *aStartOfRightContent.template ContainerAs<nsIContent>()));
 }
 
 std::ostream& operator<<(std::ostream& aStream,
                          const SplitNodeTransaction& aTransaction) {
   aStream << "{ mParentNode=" << aTransaction.mParentNode.get();
   if (aTransaction.mParentNode) {
     aStream << " (" << *aTransaction.mParentNode << ")";
   }
--- a/editor/libeditor/TypeInState.cpp
+++ b/editor/libeditor/TypeInState.cpp
@@ -309,17 +309,17 @@ void TypeInState::OnSelectionChange(cons
     AutoEditorDOMPointChildInvalidator saveOnlyOffset(mLastSelectionPoint);
   } else {
     if (aHTMLEditor.SelectionRef().RangeCount()) {
       // If selection starts from a link, we shouldn't preserve the link style
       // unless the range is entirely in the link.
       EditorRawDOMRange firstRange(*aHTMLEditor.SelectionRef().GetRangeAt(0));
       if (firstRange.StartRef().IsInContentNode() &&
           HTMLEditUtils::IsContentInclusiveDescendantOfLink(
-              *firstRange.StartRef().ContainerAsContent())) {
+              *firstRange.StartRef().ContainerAs<nsIContent>())) {
         unlink = !HTMLEditUtils::IsRangeEntirelyInLink(firstRange);
       }
     }
     mLastSelectionPoint.Clear();
   }
 
   if (resetAllStyles) {
     Reset();
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -100,25 +100,25 @@ NS_INSTANTIATE_CONST_METHOD_RETURNING_AN
 
 Result<EditorDOMPoint, nsresult>
 WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
     HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit,
     const Element& aSplittingBlockElement) {
   if (NS_WARN_IF(!aPointToSplit.IsInContentNode()) ||
       NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(aSplittingBlockElement)) ||
       NS_WARN_IF(!EditorUtils::IsEditableContent(
-          *aPointToSplit.ContainerAsContent(), EditorType::HTML))) {
+          *aPointToSplit.ContainerAs<nsIContent>(), EditorType::HTML))) {
     return Err(NS_ERROR_FAILURE);
   }
 
   // The container of aPointToSplit may be not splittable, e.g., selection
   // may be collapsed **in** a `<br>` element or a comment node.  So, look
   // for splittable point with climbing the tree up.
   EditorDOMPoint pointToSplit(aPointToSplit);
-  for (nsIContent* content : aPointToSplit.ContainerAsContent()
+  for (nsIContent* content : aPointToSplit.ContainerAs<nsIContent>()
                                  ->InclusiveAncestorsOfType<nsIContent>()) {
     if (content == &aSplittingBlockElement) {
       break;
     }
     if (HTMLEditUtils::IsSplittableNode(*content)) {
       break;
     }
     pointToSplit.Set(content);
@@ -137,21 +137,22 @@ WhiteSpaceVisibilityKeeper::PrepareToSpl
       NS_WARNING(
           "WhiteSpaceVisibilityKeeper::"
           "MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit() failed");
       return Err(rv);
     }
   }
 
   if (NS_WARN_IF(!pointToSplit.IsInContentNode()) ||
-      NS_WARN_IF(!pointToSplit.ContainerAsContent()->IsInclusiveDescendantOf(
-          &aSplittingBlockElement)) ||
+      NS_WARN_IF(
+          !pointToSplit.ContainerAs<nsIContent>()->IsInclusiveDescendantOf(
+              &aSplittingBlockElement)) ||
       NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(aSplittingBlockElement)) ||
       NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(
-          *pointToSplit.ContainerAsContent()))) {
+          *pointToSplit.ContainerAs<nsIContent>()))) {
     return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
 
   return pointToSplit;
 }
 
 // static
 EditActionResult WhiteSpaceVisibilityKeeper::
@@ -230,26 +231,26 @@ EditActionResult WhiteSpaceVisibilityKee
       NS_WARNING(
           "WhiteSpaceVisibilityKeeper::DeleteInvisibleASCIIWhiteSpaces() "
           "failed at right block child");
       return EditActionResult(rv);
     }
 
     // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
     //     Do we really need to do update rightBlockElement here??
-    // XXX And afterRightBlockChild.GetContainerAsElement() always returns
+    // XXX And afterRightBlockChild.GetContainerAs<Element>() always returns
     //     an element pointer so that probably here should not use
     //     accessors of EditorDOMPoint, should use DOM API directly instead.
-    if (afterRightBlockChild.GetContainerAsElement()) {
-      rightBlockElement = *afterRightBlockChild.GetContainerAsElement();
+    if (afterRightBlockChild.GetContainerAs<Element>()) {
+      rightBlockElement = *afterRightBlockChild.ContainerAs<Element>();
     } else if (NS_WARN_IF(
-                   !afterRightBlockChild.GetContainerParentAsElement())) {
+                   !afterRightBlockChild.GetContainerParentAs<Element>())) {
       return EditActionResult(NS_ERROR_UNEXPECTED);
     } else {
-      rightBlockElement = *afterRightBlockChild.GetContainerParentAsElement();
+      rightBlockElement = *afterRightBlockChild.GetContainerParentAs<Element>();
     }
   }
 
   // Do br adjustment.
   RefPtr<HTMLBRElement> invisibleBRElementAtEndOfLeftBlockElement =
       WSRunScanner::GetPrecedingBRElementUnlessVisibleContentFound(
           aHTMLEditor.ComputeEditingHost(),
           EditorDOMPoint::AtEndOf(aLeftBlockElement));
@@ -421,17 +422,17 @@ EditActionResult WhiteSpaceVisibilityKee
     }
   }
   if (!atLeftBlockChild.IsSetAndValid()) {
     NS_WARNING(
         "WhiteSpaceVisibilityKeeper::DeleteInvisibleASCIIWhiteSpaces() caused "
         "unexpected DOM tree");
     return EditActionResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
   }
-  // XXX atLeftBlockChild.GetContainerAsElement() should always return
+  // XXX atLeftBlockChild.GetContainerAs<Element>() should always return
   //     an element pointer so that probably here should not use
   //     accessors of EditorDOMPoint, should use DOM API directly instead.
   if (Element* nearestAncestor =
           atLeftBlockChild.GetContainerOrContainerParentElement()) {
     leftBlockElement = *nearestAncestor;
   } else {
     return EditActionResult(NS_ERROR_UNEXPECTED);
   }
@@ -870,17 +871,18 @@ CreateElementResult WhiteSpaceVisibility
       const EditorDOMPointInText atNBSPReplacedWithASCIIWhiteSpace =
           atNBSPReplacableWithSP.AsInText();
       if (!atNBSPReplacedWithASCIIWhiteSpace.IsEndOfContainer() &&
           atNBSPReplacedWithASCIIWhiteSpace.IsCharNBSP()) {
         AutoTrackDOMPoint trackPointToInsert(aHTMLEditor.RangeUpdaterRef(),
                                              &pointToInsert);
         AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
         nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
-            MOZ_KnownLive(*atNBSPReplacedWithASCIIWhiteSpace.ContainerAsText()),
+            MOZ_KnownLive(
+                *atNBSPReplacedWithASCIIWhiteSpace.ContainerAs<Text>()),
             atNBSPReplacedWithASCIIWhiteSpace.Offset(), 1, u" "_ns);
         if (MOZ_UNLIKELY(NS_FAILED(rv))) {
           NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed failed");
           return CreateElementResult(rv);
         }
         // Don't refer the following variables anymore unless tracking the
         // change.
         atNBSPReplacableWithSP.Clear();
@@ -1030,17 +1032,18 @@ Result<EditorDOMPoint, nsresult> WhiteSp
         AutoTrackDOMRange trackInvisibleLeadingWhiteSpaceRange(
             aHTMLEditor.RangeUpdaterRef(),
             &invisibleLeadingWhiteSpaceRangeAtStart);
         AutoTrackDOMRange trackInvisibleTrailingWhiteSpaceRange(
             aHTMLEditor.RangeUpdaterRef(),
             &invisibleTrailingWhiteSpaceRangeAtEnd);
         AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
         nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
-            MOZ_KnownLive(*atNBSPReplacedWithASCIIWhiteSpace.ContainerAsText()),
+            MOZ_KnownLive(
+                *atNBSPReplacedWithASCIIWhiteSpace.ContainerAs<Text>()),
             atNBSPReplacedWithASCIIWhiteSpace.Offset(), 1, u" "_ns);
         if (MOZ_UNLIKELY(NS_FAILED(rv))) {
           NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed");
           return Err(rv);
         }
       }
     }
 
@@ -1082,17 +1085,18 @@ Result<EditorDOMPoint, nsresult> WhiteSp
           atNBSPReplacedWithASCIIWhiteSpace.IsCharNBSP()) {
         AutoTrackDOMPoint trackPointToInsert(aHTMLEditor.RangeUpdaterRef(),
                                              &pointToInsert);
         AutoTrackDOMRange trackInvisibleTrailingWhiteSpaceRange(
             aHTMLEditor.RangeUpdaterRef(),
             &invisibleTrailingWhiteSpaceRangeAtEnd);
         AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
         nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
-            MOZ_KnownLive(*atNBSPReplacedWithASCIIWhiteSpace.ContainerAsText()),
+            MOZ_KnownLive(
+                *atNBSPReplacedWithASCIIWhiteSpace.ContainerAs<Text>()),
             atNBSPReplacedWithASCIIWhiteSpace.Offset(), 1, u" "_ns);
         if (MOZ_UNLIKELY(NS_FAILED(rv))) {
           NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed failed");
           return Err(rv);
         }
         // Don't refer the following variables anymore unless tracking the
         // change.
         atNBSPReplaceableWithSP.Clear();
@@ -1104,20 +1108,21 @@ Result<EditorDOMPoint, nsresult> WhiteSp
   // If white-space and/or linefeed characters are collapsible, and inserting
   // string starts and/or ends with a collapsible characters, we need to
   // replace them with NBSP for making sure the collapsible characters visible.
   // FYI: There is no case only linefeeds are collapsible.  So, we need to
   //      do the things only when white-spaces are collapsible.
   MOZ_DIAGNOSTIC_ASSERT(!theString.IsEmpty());
   if (NS_WARN_IF(!pointToInsert.IsInContentNode()) ||
       !EditorUtils::IsWhiteSpacePreformatted(
-          *pointToInsert.ContainerAsContent())) {
-    const bool isNewLineCollapsible = !pointToInsert.IsInContentNode() ||
-                                      !EditorUtils::IsNewLinePreformatted(
-                                          *pointToInsert.ContainerAsContent());
+          *pointToInsert.ContainerAs<nsIContent>())) {
+    const bool isNewLineCollapsible =
+        !pointToInsert.IsInContentNode() ||
+        !EditorUtils::IsNewLinePreformatted(
+            *pointToInsert.ContainerAs<nsIContent>());
     auto isCollapsibleChar = [&isNewLineCollapsible](char16_t aChar) -> bool {
       return nsCRT::IsAsciiSpace(aChar) &&
              (isNewLineCollapsible || aChar != HTMLEditUtils::kNewLine);
     };
     if (isCollapsibleChar(theString[0])) {
       // If inserting string will follow some invisible leading white-spaces,
       // the string needs to start with an NBSP.
       if (isInvisibleLeadingWhiteSpaceRangeAtStartPositioned) {
@@ -1461,18 +1466,17 @@ nsresult WhiteSpaceVisibilityKeeper::Del
 
   nsIContent* nextEditableSibling = HTMLEditUtils::GetNextSibling(
       *previousEditableSibling, {WalkTreeOption::IgnoreNonEditableNode});
   if (aCaretPoint.GetContainer() != nextEditableSibling) {
     return NS_OK;
   }
   EditorDOMPoint atFirstChildOfRightNode;
   rv = aHTMLEditor.JoinNearestEditableNodesWithTransaction(
-      *previousEditableSibling,
-      MOZ_KnownLive(*aCaretPoint.GetContainerAsText()),
+      *previousEditableSibling, MOZ_KnownLive(*aCaretPoint.ContainerAs<Text>()),
       &atFirstChildOfRightNode);
   if (NS_FAILED(rv)) {
     NS_WARNING("HTMLEditor::JoinNearestEditableNodesWithTransaction() failed");
     return rv;
   }
   if (!atFirstChildOfRightNode.IsSet()) {
     NS_WARNING(
         "HTMLEditor::JoinNearestEditableNodesWithTransaction() didn't return "
@@ -1588,29 +1592,30 @@ WSRunScanner::TextFragmentData::TextFrag
     NS_WARNING("aPoint was in Document or DocumentFragment");
     // I.e., we're try to modify outside of root element.  We don't need to
     // support such odd case because web apps cannot append text nodes as
     // direct child of Document node.
     return;
   }
 
   mScanStartPoint = aPoint.template To<EditorDOMPoint>();
-  NS_ASSERTION(EditorUtils::IsEditableContent(
-                   *mScanStartPoint.ContainerAsContent(), EditorType::HTML),
-               "Given content is not editable");
   NS_ASSERTION(
-      mScanStartPoint.ContainerAsContent()->GetAsElementOrParentElement(),
+      EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(),
+                                     EditorType::HTML),
+      "Given content is not editable");
+  NS_ASSERTION(
+      mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
       "Given content is not an element and an orphan node");
   if (NS_WARN_IF(!EditorUtils::IsEditableContent(
-          *mScanStartPoint.ContainerAsContent(), EditorType::HTML))) {
+          *mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML))) {
     return;
   }
   const Element* editableBlockElementOrInlineEditingHost =
       HTMLEditUtils::GetInclusiveAncestorElement(
-          *mScanStartPoint.ContainerAsContent(),
+          *mScanStartPoint.ContainerAs<nsIContent>(),
           HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost);
   if (!editableBlockElementOrInlineEditingHost) {
     NS_WARNING(
         "HTMLEditUtils::GetInclusiveAncestorElement(HTMLEditUtils::"
         "ClosestEditableBlockElementOrInlineEditingHost) couldn't find "
         "editing host");
     return;
   }
@@ -1634,21 +1639,22 @@ WSRunScanner::TextFragmentData::TextFrag
 // static
 template <typename EditorDOMPointType>
 Maybe<WSRunScanner::TextFragmentData::BoundaryData> WSRunScanner::
     TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceStartInTextNode(
         const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData) {
   MOZ_ASSERT(aPoint.IsSetAndValid());
   MOZ_DIAGNOSTIC_ASSERT(aPoint.IsInTextNode());
 
-  const bool isWhiteSpaceCollapsible =
-      !EditorUtils::IsWhiteSpacePreformatted(*aPoint.ContainerAsText());
+  const bool isWhiteSpaceCollapsible = !EditorUtils::IsWhiteSpacePreformatted(
+      *aPoint.template ContainerAs<Text>());
   const bool isNewLineCollapsible =
-      !EditorUtils::IsNewLinePreformatted(*aPoint.ContainerAsText());
-  const nsTextFragment& textFragment = aPoint.ContainerAsText()->TextFragment();
+      !EditorUtils::IsNewLinePreformatted(*aPoint.template ContainerAs<Text>());
+  const nsTextFragment& textFragment =
+      aPoint.template ContainerAs<Text>()->TextFragment();
   for (uint32_t i = std::min(aPoint.Offset(), textFragment.GetLength()); i;
        i--) {
     WSType wsTypeOfNonCollapsibleChar;
     switch (textFragment.CharAt(i - 1)) {
       case HTMLEditUtils::kSpace:
       case HTMLEditUtils::kCarriageReturn:
       case HTMLEditUtils::kTab:
         if (isWhiteSpaceCollapsible) {
@@ -1663,33 +1669,34 @@ Maybe<WSRunScanner::TextFragmentData::Bo
         }
         // preformatted linefeed.
         wsTypeOfNonCollapsibleChar = WSType::PreformattedLineBreak;
         break;
       case HTMLEditUtils::kNBSP:
         if (isWhiteSpaceCollapsible) {
           if (aNBSPData) {
             aNBSPData->NotifyNBSP(
-                EditorDOMPointInText(aPoint.ContainerAsText(), i - 1),
+                EditorDOMPointInText(aPoint.template ContainerAs<Text>(),
+                                     i - 1),
                 NoBreakingSpaceData::Scanning::Backward);
           }
           continue;
         }
         // NBSP is never converted from collapsible white-space/linefeed.
         wsTypeOfNonCollapsibleChar = WSType::NonCollapsibleCharacters;
         break;
       default:
         MOZ_ASSERT(!nsCRT::IsAsciiSpace(textFragment.CharAt(i - 1)));
         wsTypeOfNonCollapsibleChar = WSType::NonCollapsibleCharacters;
         break;
     }
 
-    return Some(BoundaryData(EditorDOMPoint(aPoint.ContainerAsText(), i),
-                             *aPoint.ContainerAsText(),
-                             wsTypeOfNonCollapsibleChar));
+    return Some(BoundaryData(
+        EditorDOMPoint(aPoint.template ContainerAs<Text>(), i),
+        *aPoint.template ContainerAs<Text>(), wsTypeOfNonCollapsibleChar));
   }
 
   return Nothing();
 }
 
 // static
 template <typename EditorDOMPointType>
 WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
@@ -1704,17 +1711,17 @@ WSRunScanner::TextFragmentData::Boundary
         BoundaryData::ScanCollapsibleWhiteSpaceStartInTextNode(aPoint,
                                                                aNBSPData);
     if (startInTextNode.isSome()) {
       return startInTextNode.ref();
     }
     // The text node does not have visible character, let's keep scanning
     // preceding nodes.
     return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
-        EditorDOMPoint(aPoint.ContainerAsText(), 0),
+        EditorDOMPoint(aPoint.template ContainerAs<Text>(), 0),
         aEditableBlockParentOrTopmostEditableInlineContent, aEditingHost,
         aNBSPData);
   }
 
   // Then, we need to check previous leaf node.
   nsIContent* previousLeafContentOrBlock =
       HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
           aPoint, aEditableBlockParentOrTopmostEditableInlineContent,
@@ -1774,21 +1781,22 @@ WSRunScanner::TextFragmentData::Boundary
 // static
 template <typename EditorDOMPointType>
 Maybe<WSRunScanner::TextFragmentData::BoundaryData> WSRunScanner::
     TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndInTextNode(
         const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData) {
   MOZ_ASSERT(aPoint.IsSetAndValid());
   MOZ_DIAGNOSTIC_ASSERT(aPoint.IsInTextNode());
 
-  const bool isWhiteSpaceCollapsible =
-      !EditorUtils::IsWhiteSpacePreformatted(*aPoint.ContainerAsText());
+  const bool isWhiteSpaceCollapsible = !EditorUtils::IsWhiteSpacePreformatted(
+      *aPoint.template ContainerAs<Text>());
   const bool isNewLineCollapsible =
-      !EditorUtils::IsNewLinePreformatted(*aPoint.ContainerAsText());
-  const nsTextFragment& textFragment = aPoint.ContainerAsText()->TextFragment();
+      !EditorUtils::IsNewLinePreformatted(*aPoint.template ContainerAs<Text>());
+  const nsTextFragment& textFragment =
+      aPoint.template ContainerAs<Text>()->TextFragment();
   for (uint32_t i = aPoint.Offset(); i < textFragment.GetLength(); i++) {
     WSType wsTypeOfNonCollapsibleChar;
     switch (textFragment.CharAt(i)) {
       case HTMLEditUtils::kSpace:
       case HTMLEditUtils::kCarriageReturn:
       case HTMLEditUtils::kTab:
         if (isWhiteSpaceCollapsible) {
           continue;  // collapsible white-space or invisible white-space.
@@ -1802,33 +1810,33 @@ Maybe<WSRunScanner::TextFragmentData::Bo
         }
         // preformatted linefeed.
         wsTypeOfNonCollapsibleChar = WSType::PreformattedLineBreak;
         break;
       case HTMLEditUtils::kNBSP:
         if (isWhiteSpaceCollapsible) {
           if (aNBSPData) {
             aNBSPData->NotifyNBSP(
-                EditorDOMPointInText(aPoint.ContainerAsText(), i),
+                EditorDOMPointInText(aPoint.template ContainerAs<Text>(), i),
                 NoBreakingSpaceData::Scanning::Forward);
           }
           continue;
         }
         // NBSP is never converted from collapsible white-space/linefeed.
         wsTypeOfNonCollapsibleChar = WSType::NonCollapsibleCharacters;
         break;
       default:
         MOZ_ASSERT(!nsCRT::IsAsciiSpace(textFragment.CharAt(i)));
         wsTypeOfNonCollapsibleChar = WSType::NonCollapsibleCharacters;
         break;
     }
 
-    return Some(BoundaryData(EditorDOMPoint(aPoint.ContainerAsText(), i),
-                             *aPoint.ContainerAsText(),
-                             wsTypeOfNonCollapsibleChar));
+    return Some(BoundaryData(
+        EditorDOMPoint(aPoint.template ContainerAs<Text>(), i),
+        *aPoint.template ContainerAs<Text>(), wsTypeOfNonCollapsibleChar));
   }
 
   return Nothing();
 }
 
 // static
 template <typename EditorDOMPointType>
 WSRunScanner::TextFragmentData::BoundaryData
@@ -1842,17 +1850,17 @@ WSRunScanner::TextFragmentData::Boundary
     Maybe<BoundaryData> endInTextNode =
         BoundaryData::ScanCollapsibleWhiteSpaceEndInTextNode(aPoint, aNBSPData);
     if (endInTextNode.isSome()) {
       return endInTextNode.ref();
     }
     // The text node does not have visible character, let's keep scanning
     // following nodes.
     return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
-        EditorDOMPointInText::AtEndOf(*aPoint.ContainerAsText()),
+        EditorDOMPointInText::AtEndOf(*aPoint.template ContainerAs<Text>()),
         aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost,
         aNBSPData);
   }
 
   // Then, we need to check next leaf node.
   nsIContent* nextLeafContentOrBlock =
       HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
           aPoint, aEditableBlockParentOrTopmostEditableInlineElement,
@@ -2563,46 +2571,47 @@ WSRunScanner::TextFragmentData::GetInclu
     point = aPoint.template To<EditorRawDOMPoint>();
   }
 
   // If it points a character in a text node, return it.
   // XXX For the performance, this does not check whether the container
   //     is outside of our range.
   if (point.IsInTextNode() && point.GetContainer()->IsEditable() &&
       !point.IsEndOfContainer()) {
-    return EditorDOMPointType(point.ContainerAsText(), point.Offset());
+    return EditorDOMPointType(point.ContainerAs<Text>(), point.Offset());
   }
 
   if (point.GetContainer() == GetEndReasonContent()) {
     return EditorDOMPointType();
   }
 
-  NS_ASSERTION(EditorUtils::IsEditableContent(
-                   *mScanStartPoint.ContainerAsContent(), EditorType::HTML),
-               "Given content is not editable");
   NS_ASSERTION(
-      mScanStartPoint.ContainerAsContent()->GetAsElementOrParentElement(),
+      EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(),
+                                     EditorType::HTML),
+      "Given content is not editable");
+  NS_ASSERTION(
+      mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
       "Given content is not an element and an orphan node");
   nsIContent* editableBlockElementOrInlineEditingHost =
-      mScanStartPoint.ContainerAsContent() &&
+      mScanStartPoint.ContainerAs<nsIContent>() &&
               EditorUtils::IsEditableContent(
-                  *mScanStartPoint.ContainerAsContent(), EditorType::HTML)
+                  *mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML)
           ? HTMLEditUtils::GetInclusiveAncestorElement(
-                *mScanStartPoint.ContainerAsContent(),
+                *mScanStartPoint.ContainerAs<nsIContent>(),
                 HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost)
           : nullptr;
   if (NS_WARN_IF(!editableBlockElementOrInlineEditingHost)) {
     // Meaning that the container of `mScanStartPoint` is not editable.
     editableBlockElementOrInlineEditingHost =
-        mScanStartPoint.ContainerAsContent();
+        mScanStartPoint.ContainerAs<nsIContent>();
   }
 
   for (nsIContent* nextContent =
            HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
-               *point.ContainerAsContent(),
+               *point.ContainerAs<nsIContent>(),
                *editableBlockElementOrInlineEditingHost,
                {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost);
        nextContent;
        nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
            *nextContent, *editableBlockElementOrInlineEditingHost,
            {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost)) {
     if (!nextContent->IsText() || !nextContent->IsEditable()) {
       if (nextContent == GetEndReasonContent()) {
@@ -2643,46 +2652,47 @@ EditorDOMPointType WSRunScanner::TextFra
   }
 
   // If it points a character in a text node and it's not first character
   // in it, return its previous point.
   // XXX For the performance, this does not check whether the container
   //     is outside of our range.
   if (point.IsInTextNode() && point.GetContainer()->IsEditable() &&
       !point.IsStartOfContainer()) {
-    return EditorDOMPointType(point.ContainerAsText(), point.Offset() - 1);
+    return EditorDOMPointType(point.ContainerAs<Text>(), point.Offset() - 1);
   }
 
   if (point.GetContainer() == GetStartReasonContent()) {
     return EditorDOMPointType();
   }
 
-  NS_ASSERTION(EditorUtils::IsEditableContent(
-                   *mScanStartPoint.ContainerAsContent(), EditorType::HTML),
-               "Given content is not editable");
   NS_ASSERTION(
-      mScanStartPoint.ContainerAsContent()->GetAsElementOrParentElement(),
+      EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(),
+                                     EditorType::HTML),
+      "Given content is not editable");
+  NS_ASSERTION(
+      mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
       "Given content is not an element and an orphan node");
   nsIContent* editableBlockElementOrInlineEditingHost =
-      mScanStartPoint.ContainerAsContent() &&
+      mScanStartPoint.ContainerAs<nsIContent>() &&
               EditorUtils::IsEditableContent(
-                  *mScanStartPoint.ContainerAsContent(), EditorType::HTML)
+                  *mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML)
           ? HTMLEditUtils::GetInclusiveAncestorElement(
-                *mScanStartPoint.ContainerAsContent(),
+                *mScanStartPoint.ContainerAs<nsIContent>(),
                 HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost)
           : nullptr;
   if (NS_WARN_IF(!editableBlockElementOrInlineEditingHost)) {
     // Meaning that the container of `mScanStartPoint` is not editable.
     editableBlockElementOrInlineEditingHost =
-        mScanStartPoint.ContainerAsContent();
+        mScanStartPoint.ContainerAs<nsIContent>();
   }
 
   for (nsIContent* previousContent =
            HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
-               *point.ContainerAsContent(),
+               *point.ContainerAs<nsIContent>(),
                *editableBlockElementOrInlineEditingHost,
                {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost);
        previousContent;
        previousContent =
            HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
                *previousContent, *editableBlockElementOrInlineEditingHost,
                {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost)) {
     if (!previousContent->IsText() || !previousContent->IsEditable()) {
@@ -2751,20 +2761,20 @@ WSRunScanner::TextFragmentData::GetEndOf
     const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
     nsIEditor::EDirection aDirectionToDelete) const {
   MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
              aDirectionToDelete == nsIEditor::eNext ||
              aDirectionToDelete == nsIEditor::ePrevious);
   MOZ_ASSERT(aPointAtASCIIWhiteSpace.IsSet());
   MOZ_ASSERT(!aPointAtASCIIWhiteSpace.IsEndOfContainer());
   MOZ_ASSERT_IF(!EditorUtils::IsNewLinePreformatted(
-                    *aPointAtASCIIWhiteSpace.ContainerAsContent()),
+                    *aPointAtASCIIWhiteSpace.ContainerAs<Text>()),
                 aPointAtASCIIWhiteSpace.IsCharCollapsibleASCIISpace());
   MOZ_ASSERT_IF(EditorUtils::IsNewLinePreformatted(
-                    *aPointAtASCIIWhiteSpace.ContainerAsContent()),
+                    *aPointAtASCIIWhiteSpace.ContainerAs<Text>()),
                 aPointAtASCIIWhiteSpace.IsCharASCIISpace());
 
   // If we're deleting text forward and the next visible character is first
   // preformatted new line but white-spaces can be collapsed, we need to
   // delete its following collapsible white-spaces too.
   bool hasSeenPreformattedNewLine =
       aPointAtASCIIWhiteSpace.IsCharPreformattedNewLine();
   auto NeedToScanFollowingWhiteSpaces =
@@ -2779,28 +2789,28 @@ WSRunScanner::TextFragmentData::GetEndOf
   auto ScanNextNonCollapsibleChar =
       [&hasSeenPreformattedNewLine, &NeedToScanFollowingWhiteSpaces](
           const EditorDOMPointInText& aPoint) -> EditorDOMPointInText {
     Maybe<uint32_t> nextVisibleCharOffset =
         HTMLEditUtils::GetNextNonCollapsibleCharOffset(aPoint);
     if (!nextVisibleCharOffset.isSome()) {
       return EditorDOMPointInText();  // Keep scanning following text nodes
     }
-    EditorDOMPointInText atNextVisibleChar(aPoint.ContainerAsText(),
+    EditorDOMPointInText atNextVisibleChar(aPoint.ContainerAs<Text>(),
                                            nextVisibleCharOffset.value());
     if (!NeedToScanFollowingWhiteSpaces(atNextVisibleChar)) {
       return atNextVisibleChar;
     }
     hasSeenPreformattedNewLine |= atNextVisibleChar.IsCharPreformattedNewLine();
     nextVisibleCharOffset =
         HTMLEditUtils::GetNextNonCollapsibleCharOffset(atNextVisibleChar);
     if (nextVisibleCharOffset.isSome()) {
-      MOZ_ASSERT(aPoint.ContainerAsText() ==
-                 atNextVisibleChar.ContainerAsText());
-      return EditorDOMPointInText(atNextVisibleChar.ContainerAsText(),
+      MOZ_ASSERT(aPoint.ContainerAs<Text>() ==
+                 atNextVisibleChar.ContainerAs<Text>());
+      return EditorDOMPointInText(atNextVisibleChar.ContainerAs<Text>(),
                                   nextVisibleCharOffset.value());
     }
     return EditorDOMPointInText();  // Keep scanning following text nodes
   };
 
   // If it's not the last character in the text node, let's scan following
   // characters in it.
   if (!aPointAtASCIIWhiteSpace.IsAtLastContent()) {
@@ -2810,18 +2820,18 @@ WSRunScanner::TextFragmentData::GetEndOf
       return atNextVisibleChar.To<EditorDOMPointType>();
     }
   }
 
   // Otherwise, i.e., the text node ends with ASCII white-space, keep scanning
   // the following text nodes.
   // XXX Perhaps, we should stop scanning if there is non-editable and visible
   //     content.
-  EditorDOMPointInText afterLastWhiteSpace =
-      EditorDOMPointInText::AtEndOf(*aPointAtASCIIWhiteSpace.ContainerAsText());
+  EditorDOMPointInText afterLastWhiteSpace = EditorDOMPointInText::AtEndOf(
+      *aPointAtASCIIWhiteSpace.ContainerAs<Text>());
   for (EditorDOMPointInText atEndOfPreviousTextNode = afterLastWhiteSpace;;) {
     const auto atStartOfNextTextNode =
         GetInclusiveNextEditableCharPoint<EditorDOMPointInText>(
             atEndOfPreviousTextNode);
     if (!atStartOfNextTextNode.IsSet()) {
       // There is no more text nodes.  Return end of the previous text node.
       return afterLastWhiteSpace.To<EditorDOMPointType>();
     }
@@ -2846,35 +2856,36 @@ WSRunScanner::TextFragmentData::GetEndOf
     const EditorDOMPointInText atNextVisibleChar(
         ScanNextNonCollapsibleChar(atStartOfNextTextNode));
     if (atNextVisibleChar.IsSet()) {
       return atNextVisibleChar.To<EditorDOMPointType>();
     }
 
     // The next text nodes ends with white-space too.  Try next one.
     afterLastWhiteSpace = atEndOfPreviousTextNode =
-        EditorDOMPointInText::AtEndOf(*atStartOfNextTextNode.ContainerAsText());
+        EditorDOMPointInText::AtEndOf(
+            *atStartOfNextTextNode.ContainerAs<Text>());
   }
 }
 
 template <typename EditorDOMPointType>
 EditorDOMPointType
 WSRunScanner::TextFragmentData::GetFirstASCIIWhiteSpacePointCollapsedTo(
     const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
     nsIEditor::EDirection aDirectionToDelete) const {
   MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
              aDirectionToDelete == nsIEditor::eNext ||
              aDirectionToDelete == nsIEditor::ePrevious);
   MOZ_ASSERT(aPointAtASCIIWhiteSpace.IsSet());
   MOZ_ASSERT(!aPointAtASCIIWhiteSpace.IsEndOfContainer());
   MOZ_ASSERT_IF(!EditorUtils::IsNewLinePreformatted(
-                    *aPointAtASCIIWhiteSpace.ContainerAsContent()),
+                    *aPointAtASCIIWhiteSpace.ContainerAs<Text>()),
                 aPointAtASCIIWhiteSpace.IsCharCollapsibleASCIISpace());
   MOZ_ASSERT_IF(EditorUtils::IsNewLinePreformatted(
-                    *aPointAtASCIIWhiteSpace.ContainerAsContent()),
+                    *aPointAtASCIIWhiteSpace.ContainerAs<Text>()),
                 aPointAtASCIIWhiteSpace.IsCharASCIISpace());
 
   // If we're deleting text backward and the previous visible character is first
   // preformatted new line but white-spaces can be collapsed, we need to delete
   // its preceding collapsible white-spaces too.
   bool hasSeenPreformattedNewLine =
       aPointAtASCIIWhiteSpace.IsCharPreformattedNewLine();
   auto NeedToScanPrecedingWhiteSpaces =
@@ -2890,30 +2901,31 @@ WSRunScanner::TextFragmentData::GetFirst
       [&hasSeenPreformattedNewLine, &NeedToScanPrecedingWhiteSpaces](
           const EditorDOMPointInText& aPoint) -> EditorDOMPointInText {
     Maybe<uint32_t> previousVisibleCharOffset =
         HTMLEditUtils::GetPreviousNonCollapsibleCharOffset(aPoint);
     if (previousVisibleCharOffset.isNothing()) {
       return EditorDOMPointInText();  // Keep scanning preceding text nodes
     }
     EditorDOMPointInText atPreviousVisibleCharacter(
-        aPoint.ContainerAsText(), previousVisibleCharOffset.value());
+        aPoint.ContainerAs<Text>(), previousVisibleCharOffset.value());
     if (!NeedToScanPrecedingWhiteSpaces(atPreviousVisibleCharacter)) {
       return atPreviousVisibleCharacter.NextPoint();
     }
     hasSeenPreformattedNewLine |=
         atPreviousVisibleCharacter.IsCharPreformattedNewLine();
     previousVisibleCharOffset =
         HTMLEditUtils::GetPreviousNonCollapsibleCharOffset(
             atPreviousVisibleCharacter);
     if (previousVisibleCharOffset.isSome()) {
-      MOZ_ASSERT(aPoint.ContainerAsText() ==
-                 atPreviousVisibleCharacter.ContainerAsText());
-      return EditorDOMPointInText(atPreviousVisibleCharacter.ContainerAsText(),
-                                  previousVisibleCharOffset.value() + 1);
+      MOZ_ASSERT(aPoint.ContainerAs<Text>() ==
+                 atPreviousVisibleCharacter.ContainerAs<Text>());
+      return EditorDOMPointInText(
+          atPreviousVisibleCharacter.ContainerAs<Text>(),
+          previousVisibleCharOffset.value() + 1);
     }
     return EditorDOMPointInText();  // Keep scanning preceding text nodes
   };
 
   // If there is some characters before it, scan it in the text node first.
   if (!aPointAtASCIIWhiteSpace.IsStartOfContainer()) {
     EditorDOMPointInText atFirstASCIIWhiteSpace(
         ScanPreviousNonCollapsibleChar(aPointAtASCIIWhiteSpace));
@@ -2922,17 +2934,17 @@ WSRunScanner::TextFragmentData::GetFirst
     }
   }
 
   // Otherwise, i.e., the text node starts with ASCII white-space, keep scanning
   // the preceding text nodes.
   // XXX Perhaps, we should stop scanning if there is non-editable and visible
   //     content.
   EditorDOMPointInText atLastWhiteSpace =
-      EditorDOMPointInText(aPointAtASCIIWhiteSpace.ContainerAsText(), 0u);
+      EditorDOMPointInText(aPointAtASCIIWhiteSpace.ContainerAs<Text>(), 0u);
   for (EditorDOMPointInText atStartOfPreviousTextNode = atLastWhiteSpace;;) {
     const EditorDOMPointInText atLastCharOfPreviousTextNode =
         GetPreviousEditableCharPoint(atStartOfPreviousTextNode);
     if (!atLastCharOfPreviousTextNode.IsSet()) {
       // There is no more text nodes.  Return end of last text node.
       return atLastWhiteSpace.To<EditorDOMPointType>();
     }
 
@@ -2953,51 +2965,51 @@ WSRunScanner::TextFragmentData::GetFirst
     const EditorDOMPointInText atFirstASCIIWhiteSpace(
         ScanPreviousNonCollapsibleChar(atLastCharOfPreviousTextNode));
     if (atFirstASCIIWhiteSpace.IsSet()) {
       return atFirstASCIIWhiteSpace.To<EditorDOMPointType>();
     }
 
     // The next text nodes starts with white-space too.  Try next one.
     atLastWhiteSpace = atStartOfPreviousTextNode = EditorDOMPointInText(
-        atLastCharOfPreviousTextNode.ContainerAsText(), 0u);
+        atLastCharOfPreviousTextNode.ContainerAs<Text>(), 0u);
   }
 }
 
 // static
 nsresult WhiteSpaceVisibilityKeeper::ReplaceTextAndRemoveEmptyTextNodes(
     HTMLEditor& aHTMLEditor, const EditorDOMRangeInTexts& aRangeToReplace,
     const nsAString& aReplaceString) {
   MOZ_ASSERT(aRangeToReplace.IsPositioned());
   MOZ_ASSERT(aRangeToReplace.StartRef().IsSetAndValid());
   MOZ_ASSERT(aRangeToReplace.EndRef().IsSetAndValid());
   MOZ_ASSERT(aRangeToReplace.StartRef().IsBefore(aRangeToReplace.EndRef()));
 
   AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
   nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
-      MOZ_KnownLive(*aRangeToReplace.StartRef().ContainerAsText()),
+      MOZ_KnownLive(*aRangeToReplace.StartRef().ContainerAs<Text>()),
       aRangeToReplace.StartRef().Offset(),
       aRangeToReplace.InSameContainer()
           ? aRangeToReplace.EndRef().Offset() -
                 aRangeToReplace.StartRef().Offset()
-          : aRangeToReplace.StartRef().ContainerAsText()->TextLength() -
+          : aRangeToReplace.StartRef().ContainerAs<Text>()->TextLength() -
                 aRangeToReplace.StartRef().Offset(),
       aReplaceString);
   if (NS_FAILED(rv)) {
     NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed");
     return rv;
   }
 
   if (aRangeToReplace.InSameContainer()) {
     return NS_OK;
   }
 
   rv = aHTMLEditor.DeleteTextAndTextNodesWithTransaction(
       EditorDOMPointInText::AtEndOf(
-          *aRangeToReplace.StartRef().ContainerAsText()),
+          *aRangeToReplace.StartRef().ContainerAs<Text>()),
       aRangeToReplace.EndRef(),
       HTMLEditor::TreatEmptyTextNodes::KeepIfContainerOfRangeBoundaries);
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rv),
       "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
   return rv;
 }
 
@@ -3010,18 +3022,18 @@ char16_t WSRunScanner::GetCharAt(Text* a
   return aTextNode->TextFragment().CharAt(aOffset);
 }
 
 // static
 template <typename EditorDOMPointType>
 nsresult WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
     HTMLEditor& aHTMLEditor, const EditorDOMPointType& aPoint) {
   MOZ_ASSERT(aPoint.IsInContentNode());
-  MOZ_ASSERT(EditorUtils::IsEditableContent(*aPoint.ContainerAsContent(),
-                                            EditorType::HTML));
+  MOZ_ASSERT(EditorUtils::IsEditableContent(
+      *aPoint.template ContainerAs<nsIContent>(), EditorType::HTML));
   Element* editingHost = aHTMLEditor.ComputeEditingHost();
   TextFragmentData textFragmentData(aPoint, editingHost);
   if (NS_WARN_IF(!textFragmentData.IsInitialized())) {
     return NS_ERROR_FAILURE;
   }
 
   // this routine examines a run of ws and tries to get rid of some unneeded
   // nbsp's, replacing them with regular ascii space if possible.  Keeping
@@ -3081,30 +3093,31 @@ nsresult WhiteSpaceVisibilityKeeper::Nor
     // i.e., unless NBSP is first character and start of a block, we may need to
     // insert <br> element and restore the NBSP to an ASCII white-space.
     if (maybeNBSPFollowsVisibleContent ||
         isPreviousCharCollapsibleASCIIWhiteSpace) {
       // First, try to insert <br> element if NBSP is at end of a block.
       // XXX We should stop this if there is a visible content.
       if (visibleWhiteSpaces.EndsByBlockBoundary() &&
           aPoint.IsInContentNode()) {
-        bool insertBRElement =
-            HTMLEditUtils::IsBlockElement(*aPoint.ContainerAsContent());
+        bool insertBRElement = HTMLEditUtils::IsBlockElement(
+            *aPoint.template ContainerAs<nsIContent>());
         if (!insertBRElement) {
-          NS_ASSERTION(EditorUtils::IsEditableContent(
-                           *aPoint.ContainerAsContent(), EditorType::HTML),
-                       "Given content is not editable");
           NS_ASSERTION(
-              aPoint.ContainerAsContent()->GetAsElementOrParentElement(),
-              "Given content is not an element and an orphan node");
+              EditorUtils::IsEditableContent(
+                  *aPoint.template ContainerAs<nsIContent>(), EditorType::HTML),
+              "Given content is not editable");
+          NS_ASSERTION(aPoint.template ContainerAs<nsIContent>()
+                           ->GetAsElementOrParentElement(),
+                       "Given content is not an element and an orphan node");
           const Element* editableBlockElement =
-              EditorUtils::IsEditableContent(*aPoint.ContainerAsContent(),
-                                             EditorType::HTML)
+              EditorUtils::IsEditableContent(
+                  *aPoint.template ContainerAs<nsIContent>(), EditorType::HTML)
                   ? HTMLEditUtils::GetInclusiveAncestorElement(
-                        *aPoint.ContainerAsContent(),
+                        *aPoint.template ContainerAs<nsIContent>(),
                         HTMLEditUtils::ClosestEditableBlockElement)
                   : nullptr;
           insertBRElement = !!editableBlockElement;
         }
         if (insertBRElement) {
           // We are at a block boundary.  Insert a <br>.  Why?  Well, first note
           // that the br will have no visible effect since it is up against a
           // block boundary.  |foo<br><p>bar| renders like |foo<p>bar| and
@@ -3170,33 +3183,33 @@ nsresult WhiteSpaceVisibilityKeeper::Nor
         }
       }
 
       // Once insert a <br>, the remaining work is only for normalizing
       // white-space sequence in white-space collapsible text node.
       // So, if the the text node's white-spaces are preformatted, we need
       // to do nothing anymore.
       if (EditorUtils::IsWhiteSpacePreformatted(
-              *atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAsText())) {
+              *atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAs<Text>())) {
         return NS_OK;
       }
 
       // Next, replace the NBSP with an ASCII white-space if it's surrounded
       // by visible contents (or immediately before a <br> element).
       // However, if it follows or is followed by a preformatted linefeed,
       // we shouldn't do this because an ASCII white-space will be collapsed
       // **into** the linefeed.
       if (maybeNBSPFollowsVisibleContent &&
           (followedByVisibleContent || followedByBRElement) &&
           !visibleWhiteSpaces.StartsFromPreformattedLineBreak()) {
         MOZ_ASSERT(!followedByPreformattedLineBreak);
         AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
         nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
             MOZ_KnownLive(
-                *atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAsText()),
+                *atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAs<Text>()),
             atPreviousCharOfEndOfVisibleWhiteSpaces.Offset(), 1, u" "_ns);
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                              "HTMLEditor::ReplaceTextWithTransaction() failed");
         return rv;
       }
     }
     // If the text node is not preformatted, and the NBSP is followed by a <br>
     // element and following (maybe multiple) collapsible ASCII white-spaces,
@@ -3205,45 +3218,45 @@ nsresult WhiteSpaceVisibilityKeeper::Nor
     // XXX This is different behavior from Blink.  Blink generates pairs of
     //     an NBSP and an ASCII white-space, but put NBSP at the end of the
     //     sequence.  We should follow the behavior for web-compat.
     if (maybeNBSPFollowsVisibleContent ||
         !isPreviousCharCollapsibleASCIIWhiteSpace ||
         !(followedByVisibleContent || followedByBRElement) ||
         EditorUtils::IsWhiteSpacePreformatted(
             *atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces
-                 .GetContainerAsText())) {
+                 .ContainerAs<Text>())) {
       return NS_OK;
     }
 
     // Currently, we're at an NBSP following an ASCII space, and we need to
     // replace them with `"&nbsp; "` for avoiding collapsing white-spaces.
     MOZ_ASSERT(!atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces
                     .IsEndOfContainer());
     const EditorDOMPointInText atFirstASCIIWhiteSpace =
         textFragmentData.GetFirstASCIIWhiteSpacePointCollapsedTo(
             atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces,
             nsIEditor::eNone);
     AutoTransactionsConserveSelection dontChangeMySelection(aHTMLEditor);
     uint32_t numberOfASCIIWhiteSpacesInStartNode =
-        atFirstASCIIWhiteSpace.ContainerAsText() ==
-                atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAsText()
+        atFirstASCIIWhiteSpace.ContainerAs<Text>() ==
+                atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAs<Text>()
             ? atPreviousCharOfEndOfVisibleWhiteSpaces.Offset() -
                   atFirstASCIIWhiteSpace.Offset()
-            : atFirstASCIIWhiteSpace.ContainerAsText()->Length() -
+            : atFirstASCIIWhiteSpace.ContainerAs<Text>()->Length() -
                   atFirstASCIIWhiteSpace.Offset();
     // Replace all preceding ASCII white-spaces **and** the NBSP.
     uint32_t replaceLengthInStartNode =
         numberOfASCIIWhiteSpacesInStartNode +
-        (atFirstASCIIWhiteSpace.ContainerAsText() ==
-                 atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAsText()
+        (atFirstASCIIWhiteSpace.ContainerAs<Text>() ==
+                 atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAs<Text>()
              ? 1
              : 0);
     nsresult rv = aHTMLEditor.ReplaceTextWithTransaction(
-        MOZ_KnownLive(*atFirstASCIIWhiteSpace.ContainerAsText()),
+        MOZ_KnownLive(*atFirstASCIIWhiteSpace.ContainerAs<Text>()),
         atFirstASCIIWhiteSpace.Offset(), replaceLengthInStartNode,
         textFragmentData.StartsFromPreformattedLineBreak() &&
                 textFragmentData.EndsByPreformattedLineBreak()
             ? u"\x00A0\x00A0"_ns
             : (textFragmentData.EndsByPreformattedLineBreak() ? u" \x00A0"_ns
                                                               : u"\x00A0 "_ns));
     if (NS_FAILED(rv)) {
       NS_WARNING("HTMLEditor::ReplaceTextWithTransaction() failed");
@@ -3255,17 +3268,17 @@ nsresult WhiteSpaceVisibilityKeeper::Nor
       return NS_OK;
     }
 
     // We need to remove the following unnecessary ASCII white-spaces and
     // NBSP at atPreviousCharOfEndOfVisibleWhiteSpaces because we collapsed them
     // into the start node.
     rv = aHTMLEditor.DeleteTextAndTextNodesWithTransaction(
         EditorDOMPointInText::AtEndOf(
-            *atFirstASCIIWhiteSpace.ContainerAsText()),
+            *atFirstASCIIWhiteSpace.ContainerAs<Text>()),
         atPreviousCharOfEndOfVisibleWhiteSpaces.NextPoint(),
         HTMLEditor::TreatEmptyTextNodes::KeepIfContainerOfRangeBoundaries);
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
@@ -3347,29 +3360,29 @@ EditorDOMPointInText WSRunScanner::TextF
   // point in the ws abut an inserted break or text, so we don't have to worry
   // about what is after it.  What is after it now will end up after the
   // inserted object.
   const EditorDOMPointInText atPreviousChar =
       GetPreviousEditableCharPoint(aPointToInsert);
   if (!atPreviousChar.IsSet() || atPreviousChar.IsEndOfContainer() ||
       !atPreviousChar.IsCharNBSP() ||
       EditorUtils::IsWhiteSpacePreformatted(
-          *atPreviousChar.ContainerAsText())) {
+          *atPreviousChar.ContainerAs<Text>())) {
     return EditorDOMPointInText();
   }
 
   const EditorDOMPointInText atPreviousCharOfPreviousChar =
       GetPreviousEditableCharPoint(atPreviousChar);
   if (atPreviousCharOfPreviousChar.IsSet()) {
     // If the previous char is in different text node and it's preformatted,
     // we shouldn't touch it.
-    if (atPreviousChar.ContainerAsText() !=
-            atPreviousCharOfPreviousChar.ContainerAsText() &&
+    if (atPreviousChar.ContainerAs<Text>() !=
+            atPreviousCharOfPreviousChar.ContainerAs<Text>() &&
         EditorUtils::IsWhiteSpacePreformatted(
-            *atPreviousCharOfPreviousChar.ContainerAsText())) {
+            *atPreviousCharOfPreviousChar.ContainerAs<Text>())) {
       return EditorDOMPointInText();
     }
     // If the previous char of the NBSP at previous position of aPointToInsert
     // is an ASCII white-space, we don't need to replace it with same character.
     if (!atPreviousCharOfPreviousChar.IsEndOfContainer() &&
         atPreviousCharOfPreviousChar.IsCharASCIISpace()) {
       return EditorDOMPointInText();
     }
@@ -3402,30 +3415,30 @@ EditorDOMPointInText WSRunScanner::TextF
   // Try to change an nbsp to a space, if possible, just to prevent nbsp
   // proliferation This routine is called when we are about to make this point
   // in the ws abut an inserted text, so we don't have to worry about what is
   // before it.  What is before it now will end up before the inserted text.
   auto atNextChar =
       GetInclusiveNextEditableCharPoint<EditorDOMPointInText>(aPointToInsert);
   if (!atNextChar.IsSet() || NS_WARN_IF(atNextChar.IsEndOfContainer()) ||
       !atNextChar.IsCharNBSP() ||
-      EditorUtils::IsWhiteSpacePreformatted(*atNextChar.ContainerAsText())) {
+      EditorUtils::IsWhiteSpacePreformatted(*atNextChar.ContainerAs<Text>())) {
     return EditorDOMPointInText();
   }
 
   const auto atNextCharOfNextCharOfNBSP =
       GetInclusiveNextEditableCharPoint<EditorDOMPointInText>(
           atNextChar.NextPoint<EditorRawDOMPointInText>());
   if (atNextCharOfNextCharOfNBSP.IsSet()) {
     // If the next char is in different text node and it's preformatted,
     // we shouldn't touch it.
-    if (atNextChar.ContainerAsText() !=
-            atNextCharOfNextCharOfNBSP.ContainerAsText() &&
+    if (atNextChar.ContainerAs<Text>() !=
+            atNextCharOfNextCharOfNBSP.ContainerAs<Text>() &&
         EditorUtils::IsWhiteSpacePreformatted(
-            *atNextCharOfNextCharOfNBSP.ContainerAsText())) {
+            *atNextCharOfNextCharOfNBSP.ContainerAs<Text>())) {
       return EditorDOMPointInText();
     }
     // If following character of an NBSP is an ASCII white-space, we don't
     // need to replace it with same character.
     if (!atNextCharOfNextCharOfNBSP.IsEndOfContainer() &&
         atNextCharOfNextCharOfNBSP.IsCharASCIISpace()) {
       return EditorDOMPointInText();
     }
@@ -4001,17 +4014,17 @@ WSRunScanner::GetRangeContainingInvisibl
       result.SetEnd(invisibleLeadingWhiteSpacesAtEnd.EndRef());
     }
     // If there is no invisible white-space and the line ends with a text
     // node, shrink the range to end of the text node.
     else if (!aRange.EndRef().IsInTextNode() &&
              textFragmentDataAtEnd.EndsByBlockBoundary() &&
              textFragmentDataAtEnd.StartRef().IsInTextNode()) {
       result.SetEnd(EditorDOMPoint::AtEndOf(
-          *textFragmentDataAtEnd.StartRef().ContainerAsText()));
+          *textFragmentDataAtEnd.StartRef().ContainerAs<Text>()));
     }
   }
   if (!result.EndRef().IsSet()) {
     result.SetEnd(aRange.EndRef());
   }
   MOZ_ASSERT(result.IsPositionedAndValid());
   return result;
 }
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -65,17 +65,17 @@ class MOZ_STACK_CLASS WSScanResult final
  public:
   WSScanResult() = delete;
   MOZ_NEVER_INLINE_DEBUG WSScanResult(nsIContent* aContent, WSType aReason)
       : mContent(aContent), mReason(aReason) {
     AssertIfInvalidData();
   }
   MOZ_NEVER_INLINE_DEBUG WSScanResult(const EditorDOMPoint& aPoint,
                                       WSType aReason)
-      : mContent(aPoint.GetContainerAsContent()),
+      : mContent(aPoint.GetContainerAs<nsIContent>()),
         mOffset(Some(aPoint.Offset())),
         mReason(aReason) {
     AssertIfInvalidData();
   }
 
   MOZ_NEVER_INLINE_DEBUG void AssertIfInvalidData() const {
 #ifdef DEBUG
     MOZ_ASSERT(mReason == WSType::UnexpectedError ||
@@ -333,34 +333,38 @@ class MOZ_STACK_CLASS WSRunScanner final
    * is at current editable character or next editable character if aPoint
    * does not points an editable character.
    */
   template <typename EditorDOMPointType = EditorDOMPointInText, typename PT,
             typename CT>
   static EditorDOMPointType GetInclusiveNextEditableCharPoint(
       Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint) {
     if (aPoint.IsInTextNode() && !aPoint.IsEndOfContainer() &&
-        HTMLEditUtils::IsSimplyEditableNode(*aPoint.ContainerAsText())) {
-      return EditorDOMPointType(aPoint.ContainerAsText(), aPoint.Offset());
+        HTMLEditUtils::IsSimplyEditableNode(
+            *aPoint.template ContainerAs<Text>())) {
+      return EditorDOMPointType(aPoint.template ContainerAs<Text>(),
+                                aPoint.Offset());
     }
     return WSRunScanner(aEditingHost, aPoint)
         .GetInclusiveNextEditableCharPoint<EditorDOMPointType>(aPoint);
   }
 
   /**
    * GetPreviousEditableCharPoint() returns a point in a text node which
    * is at previous editable character.
    */
   template <typename EditorDOMPointType = EditorDOMPointInText, typename PT,
             typename CT>
   static EditorDOMPointType GetPreviousEditableCharPoint(
       Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint) {
     if (aPoint.IsInTextNode() && !aPoint.IsStartOfContainer() &&
-        HTMLEditUtils::IsSimplyEditableNode(*aPoint.ContainerAsText())) {
-      return EditorDOMPointType(aPoint.ContainerAsText(), aPoint.Offset() - 1);
+        HTMLEditUtils::IsSimplyEditableNode(
+            *aPoint.template ContainerAs<Text>())) {
+      return EditorDOMPointType(aPoint.template ContainerAs<Text>(),
+                                aPoint.Offset() - 1);
     }
     return WSRunScanner(aEditingHost, aPoint)
         .GetPreviousEditableCharPoint<EditorDOMPointType>(aPoint);
   }
 
   /**
    * Scan aTextNode from end or start to find last or first visible things.
    * I.e., this returns a point immediately before or after invisible
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -339,17 +339,17 @@ nsresult TextServicesDocument::ExpandRan
       offsetTable.FindWordRange(
           blockStr, EditorRawDOMPoint(rngStartNode, rngStartOffset));
   offsetTable.Clear();
   if (maybeWordRange.isErr()) {
     NS_WARNING(
         "TextServicesDocument::OffsetEntryArray::FindWordRange() failed");
     return maybeWordRange.unwrapErr();
   }
-  rngStartNode = maybeWordRange.inspect().StartRef().GetContainerAsText();
+  rngStartNode = maybeWordRange.inspect().StartRef().GetContainerAs<Text>();
   rngStartOffset = maybeWordRange.inspect().StartRef().Offset();
 
   // Grab all the text in the block containing our
   // last text node.
 
   rv = docFilteredIter->PositionAt(lastText);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -369,20 +369,21 @@ nsresult TextServicesDocument::ExpandRan
         "TextServicesDocument::OffsetEntryArray::FindWordRange() failed");
     return maybeWordRange.unwrapErr();
   }
 
   // To prevent expanding the range too much, we only change
   // rngEndNode and rngEndOffset if it isn't already at the start of the
   // word and isn't equivalent to rngStartNode and rngStartOffset.
 
-  if (rngEndNode != maybeWordRange.inspect().StartRef().GetContainerAsText() ||
+  if (rngEndNode !=
+          maybeWordRange.inspect().StartRef().GetContainerAs<Text>() ||
       rngEndOffset != maybeWordRange.inspect().StartRef().Offset() ||
       (rngEndNode == rngStartNode && rngEndOffset == rngStartOffset)) {
-    rngEndNode = maybeWordRange.inspect().EndRef().GetContainerAsText();
+    rngEndNode = maybeWordRange.inspect().EndRef().GetContainerAs<Text>();
     rngEndOffset = maybeWordRange.inspect().EndRef().Offset();
   }
 
   // Now adjust the range so that it uses our new end points.
   rv = aStaticRange->SetStartAndEnd(rngStartNode, rngStartOffset, rngEndNode,
                                     rngEndOffset);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to update the given range");
   return rv;
@@ -1348,17 +1349,17 @@ void TextServicesDocument::DidJoinConten
       mOffsetTable.FirstIndexOf(*aRemovedContent.AsText());
   if (maybeRemovedIndex.isNothing()) {
     // It's okay if the node isn't in the offset table, the
     // editor could be cleaning house.
     return;
   }
 
   Maybe<size_t> maybeJoinedIndex =
-      mOffsetTable.FirstIndexOf(*aJoinedPoint.ContainerAsText());
+      mOffsetTable.FirstIndexOf(*aJoinedPoint.ContainerAs<Text>());
   if (maybeJoinedIndex.isNothing()) {
     // It's okay if the node isn't in the offset table, the
     // editor could be cleaning house.
     return;
   }
 
   const size_t removedIndex = *maybeRemovedIndex;
   const size_t joinedIndex = *maybeJoinedIndex;
@@ -1380,53 +1381,53 @@ void TextServicesDocument::DidJoinConten
   }
 
   // Run through the table and change all entries referring to
   // the removed node so that they now refer to the joined node,
   // and adjust offsets if necessary.
   const uint32_t movedTextDataLength =
       aJoinNodesDirection == JoinNodesDirection::LeftNodeIntoRightNode
           ? aJoinedPoint.Offset()
-          : aJoinedPoint.ContainerAsText()->TextDataLength() -
+          : aJoinedPoint.ContainerAs<Text>()->TextDataLength() -
                 aJoinedPoint.Offset();
   for (uint32_t i = removedIndex; i < mOffsetTable.Length(); i++) {
     const UniquePtr<OffsetEntry>& entry = mOffsetTable[i];
     LockOffsetEntryArrayLengthInDebugBuild(observer, mOffsetTable);
     if (entry->mTextNode != aRemovedContent.AsText()) {
       break;
     }
     if (entry->mIsValid) {
-      entry->mTextNode = aJoinedPoint.ContainerAsText();
+      entry->mTextNode = aJoinedPoint.ContainerAs<Text>();
       if (aJoinNodesDirection == JoinNodesDirection::RightNodeIntoLeftNode) {
         // The text was moved from aRemovedContent to end of the container of
         // aJoinedPoint.
         entry->mOffsetInTextNode += movedTextDataLength;
       }
     }
   }
 
   if (aJoinNodesDirection == JoinNodesDirection::LeftNodeIntoRightNode) {
     // The text was moved from aRemovedContent to start of the container of
     // aJoinedPoint.
     for (uint32_t i = joinedIndex; i < mOffsetTable.Length(); i++) {
       const UniquePtr<OffsetEntry>& entry = mOffsetTable[i];
       LockOffsetEntryArrayLengthInDebugBuild(observer, mOffsetTable);
-      if (entry->mTextNode != aJoinedPoint.ContainerAsText()) {
+      if (entry->mTextNode != aJoinedPoint.ContainerAs<Text>()) {
         break;
       }
       if (entry->mIsValid) {
         entry->mOffsetInTextNode += movedTextDataLength;
       }
     }
   }
 
   // Now check to see if the iterator is pointing to the
   // left node. If it is, make it point to the joined node!
   if (mFilteredIter->GetCurrentNode() == aRemovedContent.AsText()) {
-    mFilteredIter->PositionAt(aJoinedPoint.ContainerAsText());
+    mFilteredIter->PositionAt(aJoinedPoint.ContainerAs<Text>());
   }
 }
 
 nsresult TextServicesDocument::CreateFilteredContentIterator(
     const AbstractRange* aAbstractRange,
     FilteredContentIterator** aFilteredIter) {
   if (NS_WARN_IF(!aAbstractRange) || NS_WARN_IF(!aFilteredIter)) {
     return NS_ERROR_INVALID_ARG;
@@ -2666,17 +2667,17 @@ Maybe<size_t> TextServicesDocument::Offs
 Result<EditorDOMRangeInTexts, nsresult>
 TextServicesDocument::OffsetEntryArray::FindWordRange(
     nsAString& aAllTextInBlock, const EditorRawDOMPoint& aStartPointToScan) {
   MOZ_ASSERT(aStartPointToScan.IsInTextNode());
   // It's assumed that aNode is a text node. The first thing
   // we do is get its index in the offset table so we can
   // calculate the dom point's string offset.
   Maybe<size_t> maybeEntryIndex =
-      FirstIndexOf(*aStartPointToScan.ContainerAsText());
+      FirstIndexOf(*aStartPointToScan.ContainerAs<Text>());
   if (NS_WARN_IF(maybeEntryIndex.isNothing())) {
     NS_WARNING(
         "TextServicesDocument::OffsetEntryArray::FirstIndexOf() didn't find "
         "entries");
     return Err(NS_ERROR_FAILURE);
   }
 
   // Next we map offset into a string offset.
--- a/js/public/friend/StackLimits.h
+++ b/js/public/friend/StackLimits.h
@@ -158,16 +158,18 @@ class MOZ_RAII AutoCheckRecursionLimit {
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkWithStackPointerDontReport(
       JSContext* cx, void* sp) const;
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkWithStackPointerDontReport(
       JS::NativeStackLimit limit, void* sp) const;
 
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservative(JSContext* cx) const;
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservativeDontReport(
       JSContext* cx) const;
+  [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservativeDontReport(
+      JS::NativeStackLimit limit) const;
 
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystem(JSContext* cx) const;
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystemDontReport(
       JSContext* cx) const;
 };
 
 extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(JSContext* maybecx);
 extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(ErrorContext* ec);
@@ -301,11 +303,17 @@ MOZ_ALWAYS_INLINE bool AutoCheckRecursio
 MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkConservativeDontReport(
     JSContext* cx) const {
   JS::NativeStackLimit limit = getStackLimitHelper(
       cx, JS::StackForUntrustedScript, -1024 * int(sizeof(size_t)));
   int stackDummy;
   return checkLimitImpl(limit, &stackDummy);
 }
 
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkConservativeDontReport(
+    JS::NativeStackLimit limit) const {
+  int stackDummy;
+  return checkLimitImpl(limit, &stackDummy);
+}
+
 }  // namespace js
 
 #endif  // js_friend_StackLimits_h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -340,17 +340,18 @@ bool js::ExecuteRegExpLegacy(JSContext* 
 }
 
 static bool CheckPatternSyntaxSlow(JSContext* cx, Handle<JSAtom*> pattern,
                                    RegExpFlags flags) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   MainThreadErrorContext ec(cx);
   CompileOptions options(cx);
   frontend::DummyTokenStream dummyTokenStream(cx, &ec, options);
-  return irregexp::CheckPatternSyntax(cx, dummyTokenStream, pattern, flags);
+  return irregexp::CheckPatternSyntax(cx, cx->stackLimitForCurrentPrincipal(),
+                                      dummyTokenStream, pattern, flags);
 }
 
 static RegExpShared* CheckPatternSyntax(JSContext* cx, Handle<JSAtom*> pattern,
                                         RegExpFlags flags) {
   // If we already have a RegExpShared for this pattern/flags, we can
   // avoid the much slower CheckPatternSyntaxSlow call.
 
   RootedRegExpShared shared(cx, cx->zone()->regExps().maybeGet(pattern, flags));
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -176,28 +176,28 @@ class MOZ_STACK_CLASS ScriptCompiler : p
   using Base::init;
   using Base::stencil;
 
   [[nodiscard]] bool compile(JSContext* cx, SharedContext* sc);
 };
 
 #ifdef JS_ENABLE_SMOOSH
 [[nodiscard]] static bool TrySmoosh(
-    JSContext* cx, ErrorContext* ec, CompilationInput& input,
-    JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+    JSContext* cx, ErrorContext* ec, JS::NativeStackLimit stackLimit,
+    CompilationInput& input, JS::SourceText<mozilla::Utf8Unit>& srcBuf,
     UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
   MOZ_ASSERT(!stencilOut);
 
   if (!cx->options().trySmoosh()) {
     return true;
   }
 
   JSRuntime* rt = cx->runtime();
-  if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(cx, ec, input, srcBuf,
-                                                         stencilOut)) {
+  if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(
+          cx, ec, stackLimit, input, srcBuf, stencilOut)) {
     return false;
   }
 
   if (cx->options().trackNotImplemented()) {
     if (stencilOut) {
       rt->parserWatcherFile.put("1");
     } else {
       rt->parserWatcherFile.put("0");
@@ -208,18 +208,18 @@ class MOZ_STACK_CLASS ScriptCompiler : p
     fprintf(stderr, "Falling back!\n");
     return true;
   }
 
   return stencilOut->source->assignSource(cx, input.options, srcBuf);
 }
 
 [[nodiscard]] static bool TrySmoosh(
-    JSContext* cx, ErrorContext* ec, CompilationInput& input,
-    JS::SourceText<char16_t>& srcBuf,
+    JSContext* cx, ErrorContext* ec, JS::NativeStackLimit stackLimit,
+    CompilationInput& input, JS::SourceText<char16_t>& srcBuf,
     UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
   MOZ_ASSERT(!stencilOut);
   return true;
 }
 #endif  // JS_ENABLE_SMOOSH
 
 using BytecodeCompilerOutput =
     mozilla::Variant<UniquePtr<ExtensibleCompilationStencil>,
@@ -233,17 +233,17 @@ template <typename Unit>
 [[nodiscard]] static bool CompileGlobalScriptToStencilAndMaybeInstantiate(
     JSContext* cx, ErrorContext* ec, JS::NativeStackLimit stackLimit,
     js::LifoAlloc& tempLifoAlloc, CompilationInput& input,
     JS::SourceText<Unit>& srcBuf, ScopeKind scopeKind,
     BytecodeCompilerOutput& output) {
 #ifdef JS_ENABLE_SMOOSH
   {
     UniquePtr<ExtensibleCompilationStencil> extensibleStencil;
-    if (!TrySmoosh(cx, ec, input, srcBuf, extensibleStencil)) {
+    if (!TrySmoosh(cx, ec, stackLimit, input, srcBuf, extensibleStencil)) {
       return false;
     }
     if (extensibleStencil) {
       if (input.options.populateDelazificationCache() &&
           !cx->isHelperThreadContext()) {
         BorrowingCompilationStencil borrowingStencil(*extensibleStencil);
         if (!StartOffThreadDelazification(cx, input.options,
                                           borrowingStencil)) {
--- a/js/src/frontend/Frontend2.cpp
+++ b/js/src/frontend/Frontend2.cpp
@@ -29,16 +29,17 @@
 #include "frontend/TokenStream.h"       // TokenStreamAnyChars
 #include "irregexp/RegExpAPI.h"         // irregexp::CheckPatternSyntax
 #include "js/CharacterEncoding.h"  // JS::UTF8Chars, UTF8CharsToNewTwoByteCharsZ
 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
 #include "js/GCAPI.h"                 // JS::AutoCheckCannotGC
 #include "js/HeapAPI.h"               // JS::GCCellPtr
 #include "js/RegExpFlags.h"           // JS::RegExpFlag, JS::RegExpFlags
 #include "js/RootingAPI.h"            // JS::MutableHandle
+#include "js/Stack.h"                 // JS::NativeStackLimit
 #include "js/UniquePtr.h"             // js::UniquePtr
 #include "js/Utility.h"    // JS::UniqueTwoByteChars, StringBufferArena
 #include "vm/JSScript.h"   // JSScript
 #include "vm/Scope.h"      // GetScopeDataTrailingNames
 #include "vm/ScopeKind.h"  // ScopeKind
 #include "vm/SharedStencil.h"  // ImmutableScriptData, ScopeNote, TryNote, GCThingIndex
 
 using mozilla::Utf8Unit;
@@ -256,16 +257,17 @@ bool ConvertScopeStencil(JSContext* cx, 
   }
 
   return true;
 }
 
 // Given the result of SmooshMonkey's parser, convert a list of RegExp data
 // into a list of RegExpStencil.
 bool ConvertRegExpData(JSContext* cx, ErrorContext* ec,
+                       JS::NativeStackLimit stackLimit,
                        const SmooshResult& result,
                        CompilationState& compilationState) {
   auto len = result.regexps.len;
   if (len == 0) {
     return true;
   }
 
   if (len > TaggedScriptThingIndex::IndexLimit) {
@@ -316,17 +318,17 @@ bool ConvertRegExpData(JSContext* cx, Er
     mozilla::Range<const char16_t> range(pattern.get(), length);
 
     TokenStreamAnyChars ts(cx, ec, compilationState.input.options,
                            /* smg = */ nullptr);
 
     // See Parser<FullParseHandler, Unit>::newRegExp.
 
     LifoAllocScope regExpAllocScope(&cx->tempLifoAlloc());
-    if (!irregexp::CheckPatternSyntax(cx, ts, range, flags)) {
+    if (!irregexp::CheckPatternSyntax(cx, stackLimit, ts, range, flags)) {
       return false;
     }
 
     const mozilla::Utf8Unit* sUtf8 =
         reinterpret_cast<const mozilla::Utf8Unit*>(s);
     auto atom = compilationState.parserAtoms.internUtf8(cx, sUtf8, len);
     if (!atom) {
       return false;
@@ -549,18 +551,18 @@ void ReportSmooshCompileError(JSContext*
   va_start(args, errorNumber);
   ReportCompileErrorUTF8(ec, std::move(metadata), /* notes = */ nullptr,
                          errorNumber, &args);
   va_end(args);
 }
 
 /* static */
 bool Smoosh::tryCompileGlobalScriptToExtensibleStencil(
-    JSContext* cx, ErrorContext* ec, CompilationInput& input,
-    JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+    JSContext* cx, ErrorContext* ec, JS::NativeStackLimit stackLimit,
+    CompilationInput& input, JS::SourceText<mozilla::Utf8Unit>& srcBuf,
     UniquePtr<ExtensibleCompilationStencil>& stencilOut) {
   // FIXME: check info members and return with *unimplemented = true
   //        if any field doesn't match to smoosh_run.
 
   auto bytes = reinterpret_cast<const uint8_t*>(srcBuf.get());
   size_t length = srcBuf.length();
 
   SmooshCompileOptions compileOptions;
@@ -597,17 +599,17 @@ bool Smoosh::tryCompileGlobalScriptToExt
   if (!ConvertAtoms(cx, result, compilationState, allAtoms)) {
     return false;
   }
 
   if (!ConvertScopeStencil(cx, result, allAtoms, compilationState)) {
     return false;
   }
 
-  if (!ConvertRegExpData(cx, ec, result, compilationState)) {
+  if (!ConvertRegExpData(cx, ec, stackLimit, result, compilationState)) {
     return false;
   }
 
   auto len = result.scripts.len;
   if (len == 0) {
     // FIXME: What does it mean to have no scripts?
     MOZ_ASSERT(!stencilOut);
     return true;
--- a/js/src/frontend/Frontend2.h
+++ b/js/src/frontend/Frontend2.h
@@ -10,16 +10,17 @@
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include <stddef.h>  // size_t
 #include <stdint.h>  // uint8_t
 
 #include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions
 #include "js/RootingAPI.h"      // JS::Handle
 #include "js/SourceText.h"      // JS::SourceText
+#include "js/Stack.h"           // JS::NativeStackLimit
 #include "js/UniquePtr.h"       // js::UniquePtr
 #include "vm/JSScript.h"        // JSScript
 
 struct JSContext;
 
 struct SmooshResult;
 
 namespace js {
@@ -34,18 +35,18 @@ struct ExtensibleCompilationStencil;
 struct CompilationGCOutput;
 struct CompilationState;
 
 // This is declarated as a class mostly to solve dependency around `friend`
 // declarations in the simple way.
 class Smoosh {
  public:
   [[nodiscard]] static bool tryCompileGlobalScriptToExtensibleStencil(
-      JSContext* cx, ErrorContext* ec, CompilationInput& input,
-      JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+      JSContext* cx, ErrorContext* ec, JS::NativeStackLimit stackLimit,
+      CompilationInput& input, JS::SourceText<mozilla::Utf8Unit>& srcBuf,
       UniquePtr<ExtensibleCompilationStencil>& stencilOut);
 };
 
 // Initialize SmooshMonkey globals, such as the logging system.
 void InitSmoosh();
 
 // Use the SmooshMonkey frontend to parse and free the generated AST. Returns
 // true if no error were detected while parsing.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -11078,18 +11078,18 @@ RegExpLiteral* Parser<FullParseHandler, 
   uint32_t line, column;
   tokenStream.computeLineAndColumn(offset, &line, &column);
 
   if (!handler_.reuseRegexpSyntaxParse()) {
     // Verify that the Regexp will syntax parse when the time comes to
     // instantiate it. If we have already done a syntax parse, we can
     // skip this.
     LifoAllocScope parserAllocScope(&cx_->tempLifoAlloc());
-    if (!irregexp::CheckPatternSyntax(cx_, anyChars, range, flags, Some(line),
-                                      Some(column))) {
+    if (!irregexp::CheckPatternSyntax(cx_, this->stackLimit_, anyChars, range,
+                                      flags, Some(line), Some(column))) {
       return nullptr;
     }
   }
 
   auto atom =
       this->parserAtoms().internChar16(cx_, chars.begin(), chars.length());
   if (!atom) {
     return nullptr;
@@ -11121,18 +11121,18 @@ Parser<SyntaxParseHandler, Unit>::newReg
 
   uint32_t offset = anyChars.currentToken().pos.begin;
   uint32_t line, column;
   tokenStream.computeLineAndColumn(offset, &line, &column);
 
   mozilla::Range<const char16_t> source(chars.begin(), chars.length());
   {
     LifoAllocScope regExpAllocScope(&alloc_);
-    if (!irregexp::CheckPatternSyntax(cx_, anyChars, source, flags, Some(line),
-                                      Some(column))) {
+    if (!irregexp::CheckPatternSyntax(cx_, this->stackLimit_, anyChars, source,
+                                      flags, Some(line), Some(column))) {
       return null();
     }
   }
 
   return handler_.newRegExp(SyntaxParseHandler::NodeGeneric, pos());
 }
 
 template <class ParseHandler, typename Unit>
--- a/js/src/irregexp/RegExpAPI.cpp
+++ b/js/src/irregexp/RegExpAPI.cpp
@@ -146,16 +146,18 @@ static uint32_t ErrorNumber(RegExpError 
 Isolate* CreateIsolate(JSContext* cx) {
   auto isolate = MakeUnique<Isolate>(cx);
   if (!isolate || !isolate->init()) {
     return nullptr;
   }
   return isolate.release();
 }
 
+void TraceIsolate(JSTracer* trc, Isolate* isolate) { isolate->trace(trc); }
+
 void DestroyIsolate(Isolate* isolate) {
   MOZ_ASSERT(isolate->liveHandles() == 0);
   MOZ_ASSERT(isolate->livePseudoHandles() == 0);
   js_delete(isolate);
 }
 
 size_t IsolateSizeOfIncludingThis(Isolate* isolate,
                                   mozilla::MallocSizeOf mallocSizeOf) {
@@ -275,58 +277,59 @@ static void ReportSyntaxError(TokenStrea
                       pattern->latin1Chars(nogc_), pattern->length());
   } else {
     ReportSyntaxError(ts, Nothing(), Nothing(), result,
                       pattern->twoByteChars(nogc_), pattern->length());
   }
 }
 
 template <typename CharT>
-static bool CheckPatternSyntaxImpl(JSContext* cx, const CharT* input,
-                                   uint32_t inputLength, JS::RegExpFlags flags,
+static bool CheckPatternSyntaxImpl(JSContext* cx,
+                                   JS::NativeStackLimit stackLimit,
+                                   const CharT* input, uint32_t inputLength,
+                                   JS::RegExpFlags flags,
                                    RegExpCompileData* result,
                                    JS::AutoAssertNoGC& nogc) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   Zone zone(allocScope.alloc());
 
-  uintptr_t stackLimit = cx->stackLimit(JS::StackForSystemCode);
-
-  HandleScope handleScope(cx->isolate);
   return RegExpParser::VerifyRegExpSyntax(&zone, stackLimit, input, inputLength,
                                           flags, result, nogc);
 }
 
-bool CheckPatternSyntax(JSContext* cx, TokenStreamAnyChars& ts,
+bool CheckPatternSyntax(JSContext* cx, JS::NativeStackLimit stackLimit,
+                        TokenStreamAnyChars& ts,
                         const mozilla::Range<const char16_t> chars,
                         JS::RegExpFlags flags, mozilla::Maybe<uint32_t> line,
                         mozilla::Maybe<uint32_t> column) {
   RegExpCompileData result;
   JS::AutoAssertNoGC nogc(cx);
-  if (!CheckPatternSyntaxImpl(cx, chars.begin().get(), chars.length(), flags,
-                              &result, nogc)) {
+  if (!CheckPatternSyntaxImpl(cx, stackLimit, chars.begin().get(),
+                              chars.length(), flags, &result, nogc)) {
     ReportSyntaxError(ts, line, column, result, chars.begin().get(),
                       chars.length());
     return false;
   }
   return true;
 }
 
-bool CheckPatternSyntax(JSContext* cx, TokenStreamAnyChars& ts,
-                        Handle<JSAtom*> pattern, JS::RegExpFlags flags) {
+bool CheckPatternSyntax(JSContext* cx, JS::NativeStackLimit stackLimit,
+                        TokenStreamAnyChars& ts, Handle<JSAtom*> pattern,
+                        JS::RegExpFlags flags) {
   RegExpCompileData result;
   JS::AutoAssertNoGC nogc(cx);
   if (pattern->hasLatin1Chars()) {
-    if (!CheckPatternSyntaxImpl(cx, pattern->latin1Chars(nogc),
+    if (!CheckPatternSyntaxImpl(cx, stackLimit, pattern->latin1Chars(nogc),
                                 pattern->length(), flags, &result, nogc)) {
       ReportSyntaxError(ts, result, pattern);
       return false;
     }
     return true;
   }
-  if (!CheckPatternSyntaxImpl(cx, pattern->twoByteChars(nogc),
+  if (!CheckPatternSyntaxImpl(cx, stackLimit, pattern->twoByteChars(nogc),
                               pattern->length(), flags, &result, nogc)) {
     ReportSyntaxError(ts, result, pattern);
     return false;
   }
   return true;
 }
 
 // A regexp is a good candidate for Boyer-Moore if it has at least 3
--- a/js/src/irregexp/RegExpAPI.h
+++ b/js/src/irregexp/RegExpAPI.h
@@ -14,19 +14,21 @@
 #include "mozilla/Range.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "jstypes.h"
 
 #include "irregexp/RegExpTypes.h"
+#include "js/Stack.h"  // JS::NativeStackLimit
 #include "vm/RegExpShared.h"
 
 struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSTracer;
 
 namespace JS {
 class RegExpFlags;
 }
 
 namespace v8::internal {
 class RegExpStack;
 }
@@ -37,27 +39,30 @@ class VectorMatchPairs;
 
 namespace frontend {
 class TokenStreamAnyChars;
 }
 
 namespace irregexp {
 
 Isolate* CreateIsolate(JSContext* cx);
+void TraceIsolate(JSTracer* trc, Isolate* isolate);
 void DestroyIsolate(Isolate* isolate);
 
 size_t IsolateSizeOfIncludingThis(Isolate* isolate,
                                   mozilla::MallocSizeOf mallocSizeOf);
 
-bool CheckPatternSyntax(JSContext* cx, frontend::TokenStreamAnyChars& ts,
+bool CheckPatternSyntax(JSContext* cx, JS::NativeStackLimit stackLimit,
+                        frontend::TokenStreamAnyChars& ts,
                         const mozilla::Range<const char16_t> chars,
                         JS::RegExpFlags flags,
                         mozilla::Maybe<uint32_t> line = mozilla::Nothing(),
                         mozilla::Maybe<uint32_t> column = mozilla::Nothing());
-bool CheckPatternSyntax(JSContext* cx, frontend::TokenStreamAnyChars& ts,
+bool CheckPatternSyntax(JSContext* cx, JS::NativeStackLimit stackLimit,
+                        frontend::TokenStreamAnyChars& ts,
                         Handle<JSAtom*> pattern, JS::RegExpFlags flags);
 
 bool CompilePattern(JSContext* cx, MutableHandleRegExpShared re,
                     Handle<JSLinearString*> input,
                     RegExpShared::CodeKind codeKind);
 
 RegExpRunStatus Execute(JSContext* cx, MutableHandleRegExpShared re,
                         Handle<JSLinearString*> input, size_t start,
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1620,16 +1620,169 @@ function rlog_object(i) {
     var x = Math.log(o); /* Evaluated with t == i, not t == 1000 */
     t = 1000;
     if (uceFault_log_object(i) || uceFault_log_object(i))
         assertEq(x, Math.log(99) /* log(99) */);
     assertRecoveredOnBailout(x, false);
     return i;
 }
 
+var uceFault_cos_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_cos_number'));
+function rcos_number(i) {
+    var x = Math.cos(i);
+    if (uceFault_cos_number(i) || uceFault_cos_number(i))
+        assertEq(x, Math.cos(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_tan_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_tan_number'));
+function rtan_number(i) {
+    var x = Math.tan(i);
+    if (uceFault_tan_number(i) || uceFault_tan_number(i))
+        assertEq(x, Math.tan(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_exp_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_exp_number'));
+function rexp_number(i) {
+    var x = Math.exp(i);
+    if (uceFault_exp_number(i) || uceFault_exp_number(i))
+        assertEq(x, Math.exp(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_acos_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_acos_number'));
+function racos_number(i) {
+    var x = Math.acos(1 / i);
+    if (uceFault_acos_number(i) || uceFault_acos_number(i))
+        assertEq(x, Math.acos(1 / 99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_asin_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_asin_number'));
+function rasin_number(i) {
+    var x = Math.asin(1 / i);
+    if (uceFault_asin_number(i) || uceFault_asin_number(i))
+        assertEq(x, Math.asin(1 / 99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_atan_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_atan_number'));
+function ratan_number(i) {
+    var x = Math.atan(i);
+    if (uceFault_atan_number(i) || uceFault_atan_number(i))
+        assertEq(x, Math.atan(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_log10_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_log10_number'));
+function rlog10_number(i) {
+    var x = Math.log10(i);
+    if (uceFault_log10_number(i) || uceFault_log10_number(i))
+        assertEq(x, Math.log10(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_log2_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_log2_number'));
+function rlog2_number(i) {
+    var x = Math.log2(i);
+    if (uceFault_log2_number(i) || uceFault_log2_number(i))
+        assertEq(x, Math.log2(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_log1p_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_log1p_number'));
+function rlog1p_number(i) {
+    var x = Math.log1p(i);
+    if (uceFault_log1p_number(i) || uceFault_log1p_number(i))
+        assertEq(x, Math.log1p(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_expm1_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_expm1_number'));
+function rexpm1_number(i) {
+    var x = Math.expm1(i);
+    if (uceFault_expm1_number(i) || uceFault_expm1_number(i))
+        assertEq(x, Math.expm1(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_cosh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_cosh_number'));
+function rcosh_number(i) {
+    var x = Math.cosh(i);
+    if (uceFault_cosh_number(i) || uceFault_cosh_number(i))
+        assertEq(x, Math.cosh(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_sinh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_sinh_number'));
+function rsinh_number(i) {
+    var x = Math.sinh(i);
+    if (uceFault_sinh_number(i) || uceFault_sinh_number(i))
+        assertEq(x, Math.sinh(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_tanh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_tanh_number'));
+function rtanh_number(i) {
+    var x = Math.tanh(1 / i);
+    if (uceFault_tanh_number(i) || uceFault_tanh_number(i))
+        assertEq(x, Math.tanh(1 / 99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_acosh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_acosh_number'));
+function racosh_number(i) {
+    var x = Math.acosh(i);
+    if (uceFault_acosh_number(i) || uceFault_acosh_number(i))
+        assertEq(x, Math.acosh(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_asinh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_asinh_number'));
+function rasinh_number(i) {
+    var x = Math.asinh(i);
+    if (uceFault_asinh_number(i) || uceFault_asinh_number(i))
+        assertEq(x, Math.asinh(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_atanh_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_atanh_number'));
+function ratanh_number(i) {
+    var x = Math.atanh(1 / i);
+    if (uceFault_atanh_number(i) || uceFault_atanh_number(i))
+        assertEq(x, Math.atanh(1 / 99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
+var uceFault_cbrt_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_cbrt_number'));
+function rcbrt_number(i) {
+    var x = Math.cbrt(i);
+    if (uceFault_cbrt_number(i) || uceFault_cbrt_number(i))
+        assertEq(x, Math.cbrt(99));
+    assertRecoveredOnBailout(x, true);
+    return i;
+}
+
 var uceFault_sign_number = eval(`(${uceFault})`.replace('uceFault', 'uceFault_sign_number'));
 function rsign_number(i) {
     var x = Math.sign(-i - 0.12010799100);
     if (uceFault_sign_number(i) || uceFault_sign_number(i))
         assertEq(x, Math.sign(-10));
     assertRecoveredOnBailout(x, true);
     return i;
 }
@@ -1955,16 +2108,33 @@ for (j = 100 - max; j < 100; j++) {
     rhypot_object_2args(i);
     rhypot_object_3args(i);
     rhypot_object_4args(i);
     rrandom(i);
     rsin_number(i);
     rsin_object(i);
     rlog_number(i);
     rlog_object(i);
+    rcos_number(i);
+    rexp_number(i);
+    rtan_number(i);
+    racos_number(i);
+    rasin_number(i);
+    ratan_number(i);
+    rlog10_number(i);
+    rlog2_number(i);
+    rlog1p_number(i);
+    rexpm1_number(i);
+    rcosh_number(i);
+    rsinh_number(i);
+    rtanh_number(i);
+    racosh_number(i);
+    rasinh_number(i);
+    ratanh_number(i);
+    rcbrt_number(i);
     rsign_number(i);
     rsign_double(i);
     rbigintadd(BigInt(i));
     rbigintsub(BigInt(i));
     rbigintmul(BigInt(i));
     rbigintdiv(BigInt(i));
     rbigintmod(BigInt(i));
     rbigintpow(BigInt(i));
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3055,30 +3055,16 @@ bool MMathFunction::isFloat32Commutative
     case UnaryMathFunction::Round:
     case UnaryMathFunction::Trunc:
       return true;
     default:
       return false;
   }
 }
 
-bool MMathFunction::canRecoverOnBailout() const {
-  switch (function_) {
-    case UnaryMathFunction::Sin:
-    case UnaryMathFunction::Log:
-    case UnaryMathFunction::Ceil:
-    case UnaryMathFunction::Floor:
-    case UnaryMathFunction::Round:
-    case UnaryMathFunction::Trunc:
-      return true;
-    default:
-      return false;
-  }
-}
-
 MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) {
   uint32_t length = vector.length();
   MHypot* hypot = new (alloc) MHypot;
   if (!hypot->init(alloc, length)) {
     return nullptr;
   }
 
   for (uint32_t i = 0; i < length; ++i) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4818,17 +4818,17 @@ class MMathFunction : public MUnaryInstr
 
   bool isFloat32Commutative() const override;
   void trySpecializeFloat32(TempAllocator& alloc) override;
 
   void computeRange(TempAllocator& alloc) override;
 
   [[nodiscard]] bool writeRecoverData(
       CompactBufferWriter& writer) const override;
-  bool canRecoverOnBailout() const override;
+  bool canRecoverOnBailout() const override { return true; }
 
   ALLOW_CLONE(MMathFunction)
 };
 
 class MAdd : public MBinaryArithInstruction {
   MAdd(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryArithInstruction(classOpcode, left, right, type) {
     setCommutative();
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -301,18 +301,17 @@ bool RSignExtendInt32::recover(JSContext
     case MSignExtendInt32::Byte:
       result = static_cast<int8_t>(i);
       break;
     case MSignExtendInt32::Half:
       result = static_cast<int16_t>(i);
       break;
   }
 
-  RootedValue rootedResult(cx, js::Int32Value(result));
-  iter.storeInstructionResult(rootedResult);
+  iter.storeInstructionResult(JS::Int32Value(result));
   return true;
 }
 
 bool MAdd::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Add));
   writer.writeByte(type() == MIRType::Float32);
   return true;
@@ -902,25 +901,22 @@ bool RConcat::recover(JSContext* cx, Sna
 
   iter.storeInstructionResult(result);
   return true;
 }
 
 RStringLength::RStringLength(CompactBufferReader& reader) {}
 
 bool RStringLength::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue operand(cx, iter.read());
-  RootedValue result(cx);
+  JSString* string = iter.read().toString();
 
-  MOZ_ASSERT(!operand.isObject());
-  if (!js::GetLengthProperty(operand, &result)) {
-    return false;
-  }
+  static_assert(JSString::MAX_LENGTH <= INT32_MAX,
+                "Can cast string length to int32_t");
 
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(Int32Value(int32_t(string->length())));
   return true;
 }
 
 bool MStringLength::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_StringLength));
   return true;
 }
@@ -929,99 +925,87 @@ bool MArgumentsLength::writeRecoverData(
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_ArgumentsLength));
   return true;
 }
 
 RArgumentsLength::RArgumentsLength(CompactBufferReader& reader) {}
 
 bool RArgumentsLength::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue result(cx);
+  uintptr_t numActualArgs = iter.frame()->numActualArgs();
 
-  result.setInt32(iter.frame()->numActualArgs());
+  static_assert(ARGS_LENGTH_MAX <= INT32_MAX,
+                "Can cast arguments count to int32_t");
+  MOZ_ASSERT(numActualArgs <= ARGS_LENGTH_MAX);
 
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(JS::Int32Value(int32_t(numActualArgs)));
   return true;
 }
 
 bool MFloor::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
   return true;
 }
 
 RFloor::RFloor(CompactBufferReader& reader) {}
 
 bool RFloor::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue v(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_floor_impl(num);
 
-  if (!js::math_floor_handle(cx, v, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MCeil::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
   return true;
 }
 
 RCeil::RCeil(CompactBufferReader& reader) {}
 
 bool RCeil::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue v(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_ceil_impl(num);
 
-  if (!js::math_ceil_handle(cx, v, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MRound::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
   return true;
 }
 
 RRound::RRound(CompactBufferReader& reader) {}
 
 bool RRound::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue arg(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_round_impl(num);
 
-  MOZ_ASSERT(!arg.isObject());
-  if (!js::math_round_handle(cx, arg, &result)) return false;
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MTrunc::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
   return true;
 }
 
 RTrunc::RTrunc(CompactBufferReader& reader) {}
 
 bool RTrunc::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue arg(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_trunc_impl(num);
 
-  MOZ_ASSERT(!arg.isObject());
-  if (!js::math_trunc_handle(cx, arg, &result)) return false;
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MCharCodeAt::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_CharCodeAt));
   return true;
 }
@@ -1066,140 +1050,120 @@ bool MPow::writeRecoverData(CompactBuffe
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Pow));
   return true;
 }
 
 RPow::RPow(CompactBufferReader& reader) {}
 
 bool RPow::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue base(cx, iter.read());
-  RootedValue power(cx, iter.read());
-  RootedValue result(cx);
+  double base = iter.read().toNumber();
+  double power = iter.read().toNumber();
+  double result = ecmaPow(base, power);
 
-  MOZ_ASSERT(base.isNumber() && power.isNumber());
-  if (!js::PowValues(cx, &base, &power, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MPowHalf::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_PowHalf));
   return true;
 }
 
 RPowHalf::RPowHalf(CompactBufferReader& reader) {}
 
 bool RPowHalf::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue base(cx, iter.read());
-  RootedValue power(cx);
-  RootedValue result(cx);
-  power.setNumber(0.5);
+  double base = iter.read().toNumber();
+  double power = 0.5;
+  double result = ecmaPow(base, power);
 
-  MOZ_ASSERT(base.isNumber());
-  if (!js::PowValues(cx, &base, &power, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MMinMax::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax));
   writer.writeByte(isMax_);
   return true;
 }
 
 RMinMax::RMinMax(CompactBufferReader& reader) { isMax_ = reader.readByte(); }
 
 bool RMinMax::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue a(cx, iter.read());
-  RootedValue b(cx, iter.read());
-  RootedValue result(cx);
+  double x = iter.read().toNumber();
+  double y = iter.read().toNumber();
 
-  if (!js::minmax_impl(cx, isMax_, a, b, &result)) {
-    return false;
+  double result;
+  if (isMax_) {
+    result = js::math_max_impl(x, y);
+  } else {
+    result = js::math_min_impl(x, y);
   }
 
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MAbs::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Abs));
   return true;
 }
 
 RAbs::RAbs(CompactBufferReader& reader) {}
 
 bool RAbs::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue v(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_abs_impl(num);
 
-  if (!js::math_abs_handle(cx, v, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MSqrt::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Sqrt));
   writer.writeByte(type() == MIRType::Float32);
   return true;
 }
 
 RSqrt::RSqrt(CompactBufferReader& reader) {
   isFloatOperation_ = reader.readByte();
 }
 
 bool RSqrt::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue num(cx, iter.read());
-  RootedValue result(cx);
-
-  MOZ_ASSERT(num.isNumber());
-  if (!math_sqrt_handle(cx, num, &result)) {
-    return false;
-  }
+  double num = iter.read().toNumber();
+  double result = js::math_sqrt_impl(num);
 
   // MIRType::Float32 is a specialization embedding the fact that the result is
   // rounded to a Float32.
-  if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
-    return false;
+  if (isFloatOperation_) {
+    result = js::RoundFloat32(result);
   }
 
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(DoubleValue(result));
   return true;
 }
 
 bool MAtan2::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Atan2));
   return true;
 }
 
 RAtan2::RAtan2(CompactBufferReader& reader) {}
 
 bool RAtan2::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue y(cx, iter.read());
-  RootedValue x(cx, iter.read());
-  RootedValue result(cx);
+  double y = iter.read().toNumber();
+  double x = iter.read().toNumber();
+  double result = js::ecmaAtan2(y, x);
 
-  if (!math_atan2_handle(cx, y, x, &result)) return false;
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(DoubleValue(result));
   return true;
 }
 
 bool MHypot::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
   writer.writeUnsigned(uint32_t(numOperands()));
   return true;
@@ -1216,17 +1180,19 @@ bool RHypot::recover(JSContext* cx, Snap
   }
 
   for (uint32_t i = 0; i < numOperands_; ++i) {
     vec.infallibleAppend(iter.read());
   }
 
   RootedValue result(cx);
 
-  if (!js::math_hypot_handle(cx, vec, &result)) return false;
+  if (!js::math_hypot_handle(cx, vec, &result)) {
+    return false;
+  }
 
   iter.storeInstructionResult(result);
   return true;
 }
 
 bool MNearbyInt::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   switch (roundingMode_) {
@@ -1256,23 +1222,20 @@ bool MSign::writeRecoverData(CompactBuff
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Sign));
   return true;
 }
 
 RSign::RSign(CompactBufferReader& reader) {}
 
 bool RSign::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue arg(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::math_sign_impl(num);
 
-  MOZ_ASSERT(!arg.isObject());
-  if (!js::math_sign_handle(cx, arg, &result)) return false;
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(NumberValue(result));
   return true;
 }
 
 bool MMathFunction::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   switch (function_) {
     case UnaryMathFunction::Ceil:
       writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
@@ -1281,58 +1244,120 @@ bool MMathFunction::writeRecoverData(Com
       writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
       return true;
     case UnaryMathFunction::Round:
       writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
       return true;
     case UnaryMathFunction::Trunc:
       writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
       return true;
+    case UnaryMathFunction::Log:
     case UnaryMathFunction::Sin:
-    case UnaryMathFunction::Log:
+    case UnaryMathFunction::Cos:
+    case UnaryMathFunction::Exp:
+    case UnaryMathFunction::Tan:
+    case UnaryMathFunction::ACos:
+    case UnaryMathFunction::ASin:
+    case UnaryMathFunction::ATan:
+    case UnaryMathFunction::Log10:
+    case UnaryMathFunction::Log2:
+    case UnaryMathFunction::Log1P:
+    case UnaryMathFunction::ExpM1:
+    case UnaryMathFunction::CosH:
+    case UnaryMathFunction::SinH:
+    case UnaryMathFunction::TanH:
+    case UnaryMathFunction::ACosH:
+    case UnaryMathFunction::ASinH:
+    case UnaryMathFunction::ATanH:
+    case UnaryMathFunction::Cbrt:
       static_assert(sizeof(UnaryMathFunction) == sizeof(uint8_t));
       writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));
       writer.writeByte(uint8_t(function_));
       return true;
-    default:
-      MOZ_CRASH("Unknown math function.");
   }
+  MOZ_CRASH("Unknown math function.");
 }
 
 RMathFunction::RMathFunction(CompactBufferReader& reader) {
   function_ = UnaryMathFunction(reader.readByte());
 }
 
 bool RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const {
-  switch (function_) {
-    case UnaryMathFunction::Sin: {
-      RootedValue arg(cx, iter.read());
-      RootedValue result(cx);
-
-      if (!js::math_sin_handle(cx, arg, &result)) {
-        return false;
-      }
+  double num = iter.read().toNumber();
 
-      iter.storeInstructionResult(result);
-      return true;
-    }
-    case UnaryMathFunction::Log: {
-      RootedValue arg(cx, iter.read());
-      RootedValue result(cx);
+  double result;
+  switch (function_) {
+    case UnaryMathFunction::Log:
+      result = js::math_log_impl(num);
+      break;
+    case UnaryMathFunction::Sin:
+      result = js::math_sin_impl(num);
+      break;
+    case UnaryMathFunction::Cos:
+      result = js::math_cos_impl(num);
+      break;
+    case UnaryMathFunction::Exp:
+      result = js::math_exp_impl(num);
+      break;
+    case UnaryMathFunction::Tan:
+      result = js::math_tan_impl(num);
+      break;
+    case UnaryMathFunction::ACos:
+      result = js::math_acos_impl(num);
+      break;
+    case UnaryMathFunction::ASin:
+      result = js::math_asin_impl(num);
+      break;
+    case UnaryMathFunction::ATan:
+      result = js::math_atan_impl(num);
+      break;
+    case UnaryMathFunction::Log10:
+      result = js::math_log10_impl(num);
+      break;
+    case UnaryMathFunction::Log2:
+      result = js::math_log2_impl(num);
+      break;
+    case UnaryMathFunction::Log1P:
+      result = js::math_log1p_impl(num);
+      break;
+    case UnaryMathFunction::ExpM1:
+      result = js::math_expm1_impl(num);
+      break;
+    case UnaryMathFunction::CosH:
+      result = js::math_cosh_impl(num);
+      break;
+    case UnaryMathFunction::SinH:
+      result = js::math_sinh_impl(num);
+      break;
+    case UnaryMathFunction::TanH:
+      result = js::math_tanh_impl(num);
+      break;
+    case UnaryMathFunction::ACosH:
+      result = js::math_acosh_impl(num);
+      break;
+    case UnaryMathFunction::ASinH:
+      result = js::math_asinh_impl(num);
+      break;
+    case UnaryMathFunction::ATanH:
+      result = js::math_atanh_impl(num);
+      break;
+    case UnaryMathFunction::Cbrt:
+      result = js::math_cbrt_impl(num);
+      break;
 
-      if (!js::math_log_handle(cx, arg, &result)) {
-        return false;
-      }
+    case UnaryMathFunction::Trunc:
+    case UnaryMathFunction::Floor:
+    case UnaryMathFunction::Ceil:
+    case UnaryMathFunction::Round:
+      // These have their own recover instructions.
+      MOZ_CRASH("Unexpected rounding math function.");
+  }
 
-      iter.storeInstructionResult(result);
-      return true;
-    }
-    default:
-      MOZ_CRASH("Unknown math function.");
-  }
+  iter.storeInstructionResult(DoubleValue(result));
+  return true;
 }
 
 bool MRandom::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(this->canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
   return true;
 }
 
@@ -1372,28 +1397,22 @@ bool MNaNToZero::writeRecoverData(Compac
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NaNToZero));
   return true;
 }
 
 RNaNToZero::RNaNToZero(CompactBufferReader& reader) {}
 
 bool RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue v(cx, iter.read());
-  RootedValue result(cx);
-  MOZ_ASSERT(v.isDouble() || v.isInt32());
-
-  // x ? x : 0.0
-  if (ToBoolean(v)) {
-    result = v;
-  } else {
-    result.setDouble(0.0);
+  double v = iter.read().toNumber();
+  if (mozilla::IsNaN(v)) {
+    v = 0.0;
   }
 
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(DoubleValue(v));
   return true;
 }
 
 bool MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
   return true;
 }
@@ -1427,19 +1446,17 @@ bool RRegExpSearcher::recover(JSContext*
   RootedString input(cx, iter.read().toString());
   int32_t lastIndex = iter.read().toInt32();
 
   int32_t result;
   if (!RegExpSearcherRaw(cx, regexp, input, lastIndex, nullptr, &result)) {
     return false;
   }
 
-  RootedValue resultVal(cx);
-  resultVal.setInt32(result);
-  iter.storeInstructionResult(resultVal);
+  iter.storeInstructionResult(Int32Value(result));
   return true;
 }
 
 bool MRegExpTester::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTester));
   return true;
 }
@@ -1451,19 +1468,17 @@ bool RRegExpTester::recover(JSContext* c
   RootedObject regexp(cx, &iter.read().toObject());
   int32_t lastIndex = iter.read().toInt32();
   int32_t endIndex;
 
   if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, &endIndex)) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setInt32(endIndex);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(Int32Value(endIndex));
   return true;
 }
 
 bool MTypeOf::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOf));
   return true;
 }
@@ -1499,71 +1514,63 @@ bool MToDouble::writeRecoverData(Compact
   writer.writeUnsigned(uint32_t(RInstruction::Recover_ToDouble));
   return true;
 }
 
 RToDouble::RToDouble(CompactBufferReader& reader) {}
 
 bool RToDouble::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedValue v(cx, iter.read());
-  RootedValue result(cx);
 
   MOZ_ASSERT(!v.isObject());
   MOZ_ASSERT(!v.isSymbol());
+  MOZ_ASSERT(!v.isBigInt());
 
   double dbl;
   if (!ToNumber(cx, v, &dbl)) {
     return false;
   }
 
-  result.setDouble(dbl);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(DoubleValue(dbl));
   return true;
 }
 
 bool MToFloat32::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_ToFloat32));
   return true;
 }
 
 RToFloat32::RToFloat32(CompactBufferReader& reader) {}
 
 bool RToFloat32::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue v(cx, iter.read());
-  RootedValue result(cx);
+  double num = iter.read().toNumber();
+  double result = js::RoundFloat32(num);
 
-  MOZ_ASSERT(!v.isObject());
-  if (!RoundFloat32(cx, v, &result)) {
-    return false;
-  }
-
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(DoubleValue(result));
   return true;
 }
 
 bool MTruncateToInt32::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_TruncateToInt32));
   return true;
 }
 
 RTruncateToInt32::RTruncateToInt32(CompactBufferReader& reader) {}
 
 bool RTruncateToInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedValue value(cx, iter.read());
-  RootedValue result(cx);
 
   int32_t trunc;
   if (!JS::ToInt32(cx, value, &trunc)) {
     return false;
   }
 
-  result.setInt32(trunc);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(Int32Value(trunc));
   return true;
 }
 
 bool MNewObject::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
 
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
 
@@ -1571,28 +1578,26 @@ bool MNewObject::writeRecoverData(Compac
   MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
   return true;
 }
 
 RNewObject::RNewObject(CompactBufferReader& reader) {}
 
 bool RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject templateObject(cx, &iter.read().toObject());
-  RootedValue result(cx);
 
   // See CodeGenerator::visitNewObjectVMCall.
   // Note that recover instructions are only used if mode == ObjectCreate.
   JSObject* resultObject =
       ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>());
   if (!resultObject) {
     return false;
   }
 
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MNewPlainObject::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewPlainObject));
 
   MOZ_ASSERT(gc::AllocKind(uint8_t(allocKind_)) == allocKind_);
@@ -1615,19 +1620,17 @@ bool RNewPlainObject::recover(JSContext*
 
   // See CodeGenerator::visitNewPlainObject.
   JSObject* resultObject =
       NewPlainObjectOptimizedFallback(cx, shape, allocKind_, initialHeap_);
   if (!resultObject) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MNewArrayObject::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayObject));
 
   writer.writeUnsigned(length_);
@@ -1648,89 +1651,82 @@ bool RNewArrayObject::recover(JSContext*
 
   NewObjectKind kind =
       initialHeap_ == gc::TenuredHeap ? TenuredObject : GenericObject;
   JSObject* array = NewArrayOperation(cx, length_, kind);
   if (!array) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setObject(*array);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*array));
   return true;
 }
 
 bool MNewTypedArray::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewTypedArray));
   return true;
 }
 
 RNewTypedArray::RNewTypedArray(CompactBufferReader& reader) {}
 
 bool RNewTypedArray::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject templateObject(cx, &iter.read().toObject());
-  RootedValue result(cx);
 
   size_t length = templateObject.as<TypedArrayObject>()->length();
   MOZ_ASSERT(length <= INT32_MAX,
              "Template objects are only created for int32 lengths");
 
   JSObject* resultObject =
       NewTypedArrayWithTemplateAndLength(cx, templateObject, length);
   if (!resultObject) {
     return false;
   }
 
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MNewArray::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
   writer.writeUnsigned(length());
   return true;
 }
 
 RNewArray::RNewArray(CompactBufferReader& reader) {
   count_ = reader.readUnsigned();
 }
 
 bool RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject templateObject(cx, &iter.read().toObject());
-  RootedValue result(cx);
   Rooted<Shape*> shape(cx, templateObject->shape());
 
   ArrayObject* resultObject = NewArrayWithShape(cx, count_, shape);
   if (!resultObject) {
     return false;
   }
 
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MNewIterator::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewIterator));
   writer.writeByte(type_);
   return true;
 }
 
 RNewIterator::RNewIterator(CompactBufferReader& reader) {
   type_ = reader.readByte();
 }
 
 bool RNewIterator::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject templateObject(cx, &iter.read().toObject());
-  RootedValue result(cx);
 
   JSObject* resultObject = nullptr;
   switch (MNewIterator::Type(type_)) {
     case MNewIterator::ArrayIterator:
       resultObject = NewArrayIterator(cx);
       break;
     case MNewIterator::StringIterator:
       resultObject = NewStringIterator(cx);
@@ -1739,18 +1735,17 @@ bool RNewIterator::recover(JSContext* cx
       resultObject = NewRegExpStringIterator(cx);
       break;
   }
 
   if (!resultObject) {
     return false;
   }
 
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MLambda::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_Lambda));
   return true;
 }
@@ -1761,19 +1756,17 @@ bool RLambda::recover(JSContext* cx, Sna
   RootedObject scopeChain(cx, &iter.read().toObject());
   RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
 
   JSObject* resultObject = js::Lambda(cx, fun, scopeChain);
   if (!resultObject) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MFunctionWithProto::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_FunctionWithProto));
   return true;
 }
@@ -1786,19 +1779,17 @@ bool RFunctionWithProto::recover(JSConte
   RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
 
   JSObject* resultObject =
       js::FunWithProtoOperation(cx, fun, scopeChain, prototype);
   if (!resultObject) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MNewCallObject::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewCallObject));
   return true;
 }
@@ -1810,62 +1801,57 @@ bool RNewCallObject::recover(JSContext* 
 
   Rooted<Shape*> shape(cx, templateObj->shape());
 
   JSObject* resultObject = NewCallObject(cx, shape);
   if (!resultObject) {
     return false;
   }
 
-  RootedValue result(cx);
-  result.setObject(*resultObject);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*resultObject));
   return true;
 }
 
 bool MObjectState::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectState));
   writer.writeUnsigned(numSlots());
   return true;
 }
 
 RObjectState::RObjectState(CompactBufferReader& reader) {
   numSlots_ = reader.readUnsigned();
 }
 
 bool RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject object(cx, &iter.read().toObject());
-  RootedValue val(cx);
-  Rooted<NativeObject*> nativeObject(cx, &object->as<NativeObject>());
+  Handle<NativeObject*> nativeObject = object.as<NativeObject>();
   MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
 
   for (size_t i = 0; i < numSlots(); i++) {
-    val = iter.read();
+    Value val = iter.read();
     nativeObject->setSlot(i, val);
   }
 
-  val.setObject(*object);
-  iter.storeInstructionResult(val);
+  iter.storeInstructionResult(ObjectValue(*object));
   return true;
 }
 
 bool MArrayState::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_ArrayState));
   writer.writeUnsigned(numElements());
   return true;
 }
 
 RArrayState::RArrayState(CompactBufferReader& reader) {
   numElements_ = reader.readUnsigned();
 }
 
 bool RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue result(cx);
   ArrayObject* object = &iter.read().toObject().as<ArrayObject>();
   uint32_t initLength = iter.read().toInt32();
 
   MOZ_ASSERT(object->getDenseInitializedLength() == 0,
              "initDenseElement call below relies on this");
   object->setDenseInitializedLength(initLength);
 
   for (size_t index = 0; index < numElements(); index++) {
@@ -1874,18 +1860,17 @@ bool RArrayState::recover(JSContext* cx,
     if (index >= initLength) {
       MOZ_ASSERT(val.isUndefined());
       continue;
     }
 
     object->initDenseElement(index, val);
   }
 
-  result.setObject(*object);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*object));
   return true;
 }
 
 bool MSetArrayLength::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   // For simplicity, we capture directly the object instead of the elements
   // pointer.
   MOZ_ASSERT(elements()->type() != MIRType::Elements);
@@ -1895,30 +1880,28 @@ bool MSetArrayLength::writeRecoverData(C
 
 bool MSetArrayLength::canRecoverOnBailout() const {
   return isRecoveredOnBailout();
 }
 
 RSetArrayLength::RSetArrayLength(CompactBufferReader& reader) {}
 
 bool RSetArrayLength::recover(JSContext* cx, SnapshotIterator& iter) const {
-  RootedValue result(cx);
   Rooted<ArrayObject*> obj(cx, &iter.read().toObject().as<ArrayObject>());
   RootedValue len(cx, iter.read());
 
   RootedId id(cx, NameToId(cx->names().length));
   Rooted<PropertyDescriptor> desc(
       cx, PropertyDescriptor::Data(len, JS::PropertyAttribute::Writable));
   ObjectOpResult error;
   if (!ArraySetLength(cx, obj, id, desc, error)) {
     return false;
   }
 
-  result.setObject(*obj);
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(ObjectValue(*obj));
   return true;
 }
 
 bool MAssertRecoveredOnBailout::writeRecoverData(
     CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   MOZ_RELEASE_ASSERT(input()->isRecoveredOnBailout() == mustBeRecovered_,
                      "assertRecoveredOnBailout failed during compilation");
@@ -1927,20 +1910,18 @@ bool MAssertRecoveredOnBailout::writeRec
   return true;
 }
 
 RAssertRecoveredOnBailout::RAssertRecoveredOnBailout(
     CompactBufferReader& reader) {}
 
 bool RAssertRecoveredOnBailout::recover(JSContext* cx,
                                         SnapshotIterator& iter) const {
-  RootedValue result(cx);
   iter.read();  // skip the unused operand.
-  result.setUndefined();
-  iter.storeInstructionResult(result);
+  iter.storeInstructionResult(UndefinedValue());
   return true;
 }
 
 bool MStringReplace::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
   writer.writeByte(isFlatReplacement_);
   return true;
@@ -2002,18 +1983,17 @@ bool RAtomicIsLockFree::recover(JSContex
   RootedValue operand(cx, iter.read());
   MOZ_ASSERT(operand.isInt32());
 
   int32_t result;
   if (!js::AtomicIsLockFree(cx, operand, &result)) {
     return false;
   }
 
-  RootedValue rootedResult(cx, js::Int32Value(result));
-  iter.storeInstructionResult(rootedResult);
+  iter.storeInstructionResult(Int32Value(result));
   return true;
 }
 
 bool MBigIntAsIntN::writeRecoverData(CompactBufferWriter& writer) const {
   MOZ_ASSERT(canRecoverOnBailout());
   writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntAsIntN));
   return true;
 }
@@ -2097,18 +2077,18 @@ bool RCreateInlinedArgumentsObject::reco
   RootedObject callObject(cx, &iter.read().toObject());
   RootedFunction callee(cx, &iter.read().toObject().as<JSFunction>());
 
   JS::RootedValueArray<ArgumentsObject::MaxInlinedArgs> argsArray(cx);
   for (uint32_t i = 0; i < numActuals_; i++) {
     argsArray[i].set(iter.read());
   }
 
-  RootedObject result(cx, ArgumentsObject::createFromValueArray(
-                              cx, argsArray, callee, callObject, numActuals_));
+  ArgumentsObject* result = ArgumentsObject::createFromValueArray(
+      cx, argsArray, callee, callObject, numActuals_);
   if (!result) {
     return false;
   }
 
   iter.storeInstructionResult(JS::ObjectValue(*result));
   return true;
 }
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -31,17 +31,16 @@
 #include "vm/WellKnownAtom.h"  // js_*_str
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 using JS::ToNumber;
-using mozilla::Abs;
 using mozilla::ExponentComponent;
 using mozilla::FloatingPoint;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::Maybe;
@@ -53,148 +52,130 @@ using mozilla::WrappingMultiply;
 
 static mozilla::Atomic<bool, mozilla::Relaxed> sUseFdlibmForSinCosTan;
 
 JS_PUBLIC_API void JS::SetUseFdlibmForSinCosTan(bool value) {
   sUseFdlibmForSinCosTan = value;
 }
 
 template <UnaryMathFunctionType F>
-static bool math_function(JSContext* cx, HandleValue val,
-                          MutableHandleValue res) {
-  double x;
-  if (!ToNumber(cx, val, &x)) {
-    return false;
-  }
-
-  // NB: Always stored as a double so the math function can be inlined
-  // through MMathFunction. We also rely on this to avoid type monitoring
-  // in CallIRGenerator::tryAttachMathSqrt.
-  double z = F(x);
-  res.setDouble(z);
-  return true;
-}
-
-template <UnaryMathFunctionType F>
 static bool math_function(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_function<F>(cx, args[0], args.rval());
-}
-
-bool js::math_abs_handle(JSContext* cx, js::HandleValue v,
-                         js::MutableHandleValue r) {
   double x;
-  if (!ToNumber(cx, v, &x)) {
+  if (!ToNumber(cx, args[0], &x)) {
     return false;
   }
 
-  double z = Abs(x);
-  r.setNumber(z);
+  // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always
+  // type the value as a double.
 
+  // NB: Always stored as a double so the math function can be inlined
+  // through MMathFunction.
+  double z = F(x);
+  args.rval().setDouble(z);
   return true;
 }
 
+double js::math_abs_impl(double x) { return mozilla::Abs(x); }
+
 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_abs_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_abs_impl(x));
+  return true;
 }
 
 double js::math_acos_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::acos(x);
 }
 
-bool js::math_acos(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_acos(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_acos_impl>(cx, argc, vp);
 }
 
 double js::math_asin_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::asin(x);
 }
 
-bool js::math_asin(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_asin(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_asin_impl>(cx, argc, vp);
 }
 
 double js::math_atan_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::atan(x);
 }
 
-bool js::math_atan(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_atan(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_atan_impl>(cx, argc, vp);
 }
 
 double js::ecmaAtan2(double y, double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::atan2(y, x);
 }
 
-bool js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x,
-                           MutableHandleValue res) {
-  double dy;
-  if (!ToNumber(cx, y, &dy)) {
+static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  double y;
+  if (!ToNumber(cx, args.get(0), &y)) {
     return false;
   }
 
-  double dx;
-  if (!ToNumber(cx, x, &dx)) {
+  double x;
+  if (!ToNumber(cx, args.get(1), &x)) {
     return false;
   }
 
-  double z = ecmaAtan2(dy, dx);
-  res.setDouble(z);
+  double z = ecmaAtan2(y, x);
+  args.rval().setDouble(z);
   return true;
 }
 
-bool js::math_atan2(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
-}
-
 double js::math_ceil_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::ceil(x);
 }
 
-bool js::math_ceil_handle(JSContext* cx, HandleValue v,
-                          MutableHandleValue res) {
-  double d;
-  if (!ToNumber(cx, v, &d)) return false;
-
-  double result = math_ceil_impl(d);
-  res.setNumber(result);
-  return true;
-}
-
-bool js::math_ceil(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_ceil_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_ceil_impl(x));
+  return true;
 }
 
-bool js::math_clz32(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setInt32(32);
     return true;
   }
 
   uint32_t n;
@@ -220,75 +201,76 @@ double js::math_cos_fdlibm_impl(double x
 }
 
 double js::math_cos_native_impl(double x) {
   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
   AutoUnsafeCallWithABI unsafe;
   return std::cos(x);
 }
 
-bool js::math_cos(JSContext* cx, unsigned argc, Value* vp) {
+double js::math_cos_impl(double x) {
+  if (sUseFdlibmForSinCosTan) {
+    return math_cos_fdlibm_impl(x);
+  }
+  return math_cos_native_impl(x);
+}
+
+static bool math_cos(JSContext* cx, unsigned argc, Value* vp) {
   if (sUseFdlibmForSinCosTan) {
     return math_function<math_cos_fdlibm_impl>(cx, argc, vp);
   }
   return math_function<math_cos_native_impl>(cx, argc, vp);
 }
 
 double js::math_exp_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::exp(x);
 }
 
-bool js::math_exp(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_exp(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_exp_impl>(cx, argc, vp);
 }
 
 double js::math_floor_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::floor(x);
 }
 
-bool js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
-  double d;
-  if (!ToNumber(cx, v, &d)) {
-    return false;
-  }
-
-  double z = math_floor_impl(d);
-  r.setNumber(z);
-
-  return true;
-}
-
 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_floor_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_floor_impl(x));
+  return true;
 }
 
 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
                           MutableHandleValue res) {
   int32_t a = 0, b = 0;
   if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
     return false;
   }
   if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
     return false;
   }
 
   res.setInt32(WrappingMultiply(a, b));
   return true;
 }
 
-bool js::math_imul(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_imul(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
 }
 
 // Implements Math.fround (20.2.2.16) up to step 3
 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
   double d;
@@ -302,38 +284,37 @@ bool js::RoundFloat32(JSContext* cx, Han
   if (!RoundFloat32(cx, arg, &f)) {
     return false;
   }
 
   res.setDouble(static_cast<double>(f));
   return true;
 }
 
-bool js::math_fround(JSContext* cx, unsigned argc, Value* vp) {
+double js::RoundFloat32(double d) {
+  return static_cast<double>(static_cast<float>(d));
+}
+
+static bool math_fround(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
   return RoundFloat32(cx, args[0], args.rval());
 }
 
 double js::math_log_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::log(x);
 }
 
-bool js::math_log_handle(JSContext* cx, HandleValue val,
-                         MutableHandleValue res) {
-  return math_function<math_log_impl>(cx, val, res);
-}
-
-bool js::math_log(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_log(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_log_impl>(cx, argc, vp);
 }
 
 double js::math_max_impl(double x, double y) {
   AutoUnsafeCallWithABI unsafe;
 
   // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
   if (x > y || IsNaN(x) || (x == y && IsNegative(y))) {
@@ -377,36 +358,16 @@ bool js::math_min(JSContext* cx, unsigne
       return false;
     }
     minval = math_min_impl(x, minval);
   }
   args.rval().setNumber(minval);
   return true;
 }
 
-bool js::minmax_impl(JSContext* cx, bool max, HandleValue a, HandleValue b,
-                     MutableHandleValue res) {
-  double x, y;
-
-  if (!ToNumber(cx, a, &x)) {
-    return false;
-  }
-  if (!ToNumber(cx, b, &y)) {
-    return false;
-  }
-
-  if (max) {
-    res.setNumber(math_max_impl(x, y));
-  } else {
-    res.setNumber(math_min_impl(x, y));
-  }
-
-  return true;
-}
-
 double js::powi(double x, int32_t y) {
   AutoUnsafeCallWithABI unsafe;
 
   // It's only safe to optimize this when we can compute with integer values or
   // the exponent is a small, positive constant.
   if (y >= 0) {
     uint32_t n = uint32_t(y);
 
@@ -500,17 +461,17 @@ double js::ecmaPow(double x, double y) {
     }
     if (y == -0.5) {
       return 1.0 / std::sqrt(x);
     }
   }
   return std::pow(x, y);
 }
 
-bool js::math_pow(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_pow(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   double x;
   if (!ToNumber(cx, args.get(0), &x)) {
     return false;
   }
 
   double y;
@@ -551,34 +512,22 @@ Realm::getOrCreateRandomNumberGenerator(
 
   return randomNumberGenerator_.ref();
 }
 
 double js::math_random_impl(JSContext* cx) {
   return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
 }
 
-bool js::math_random(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_random(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setDouble(math_random_impl(cx));
   return true;
 }
 
-bool js::math_round_handle(JSContext* cx, HandleValue arg,
-                           MutableHandleValue res) {
-  double d;
-  if (!ToNumber(cx, arg, &d)) {
-    return false;
-  }
-
-  d = math_round_impl(d);
-  res.setNumber(d);
-  return true;
-}
-
 template <typename T>
 T js::GetBiggestNumberLessThan(T x) {
   MOZ_ASSERT(!IsNegative(x));
   MOZ_ASSERT(IsFinite(x));
   using Bits = typename mozilla::FloatingPoint<T>::Bits;
   Bits bits = mozilla::BitwiseCast<Bits>(x);
   MOZ_ASSERT(bits > 0, "will underflow");
   return mozilla::BitwiseCast<T>(bits - 1);
@@ -618,175 +567,182 @@ float js::math_roundf_impl(float x) {
       int_fast16_t(FloatingPoint<float>::kExponentShift)) {
     return x;
   }
 
   float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
   return std::copysign(fdlibm::floorf(x + add), x);
 }
 
-bool /* ES5 15.8.2.15. */
-js::math_round(JSContext* cx, unsigned argc, Value* vp) {
+/* ES5 15.8.2.15. */
+static bool math_round(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_round_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_round_impl(x));
+  return true;
 }
 
 double js::math_sin_fdlibm_impl(double x) {
   MOZ_ASSERT(sUseFdlibmForSinCosTan);
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::sin(x);
 }
 
 double js::math_sin_native_impl(double x) {
   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
   AutoUnsafeCallWithABI unsafe;
   return std::sin(x);
 }
 
-bool js::math_sin_handle(JSContext* cx, HandleValue val,
-                         MutableHandleValue res) {
+double js::math_sin_impl(double x) {
   if (sUseFdlibmForSinCosTan) {
-    return math_function<math_sin_fdlibm_impl>(cx, val, res);
+    return math_sin_fdlibm_impl(x);
   }
-  return math_function<math_sin_native_impl>(cx, val, res);
+  return math_sin_native_impl(x);
 }
 
-bool js::math_sin(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_sin(JSContext* cx, unsigned argc, Value* vp) {
   if (sUseFdlibmForSinCosTan) {
     return math_function<math_sin_fdlibm_impl>(cx, argc, vp);
   }
   return math_function<math_sin_native_impl>(cx, argc, vp);
 }
 
 double js::math_sqrt_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return std::sqrt(x);
 }
 
-bool js::math_sqrt_handle(JSContext* cx, HandleValue number,
-                          MutableHandleValue result) {
-  return math_function<math_sqrt_impl>(cx, number, result);
-}
-
-bool js::math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_sqrt_impl>(cx, argc, vp);
 }
 
 double js::math_tan_fdlibm_impl(double x) {
   MOZ_ASSERT(sUseFdlibmForSinCosTan);
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::tan(x);
 }
 
 double js::math_tan_native_impl(double x) {
   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
   AutoUnsafeCallWithABI unsafe;
   return std::tan(x);
 }
 
-bool js::math_tan(JSContext* cx, unsigned argc, Value* vp) {
+double js::math_tan_impl(double x) {
+  if (sUseFdlibmForSinCosTan) {
+    return math_tan_fdlibm_impl(x);
+  }
+  return math_tan_native_impl(x);
+}
+
+static bool math_tan(JSContext* cx, unsigned argc, Value* vp) {
   if (sUseFdlibmForSinCosTan) {
     return math_function<math_tan_fdlibm_impl>(cx, argc, vp);
   }
   return math_function<math_tan_native_impl>(cx, argc, vp);
 }
 
 double js::math_log10_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::log10(x);
 }
 
-bool js::math_log10(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_log10(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_log10_impl>(cx, argc, vp);
 }
 
 double js::math_log2_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::log2(x);
 }
 
-bool js::math_log2(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_log2(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_log2_impl>(cx, argc, vp);
 }
 
 double js::math_log1p_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::log1p(x);
 }
 
-bool js::math_log1p(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_log1p_impl>(cx, argc, vp);
 }
 
 double js::math_expm1_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::expm1(x);
 }
 
-bool js::math_expm1(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_expm1_impl>(cx, argc, vp);
 }
 
 double js::math_cosh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::cosh(x);
 }
 
-bool js::math_cosh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_cosh_impl>(cx, argc, vp);
 }
 
 double js::math_sinh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::sinh(x);
 }
 
-bool js::math_sinh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_sinh_impl>(cx, argc, vp);
 }
 
 double js::math_tanh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::tanh(x);
 }
 
-bool js::math_tanh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_tanh_impl>(cx, argc, vp);
 }
 
 double js::math_acosh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::acosh(x);
 }
 
-bool js::math_acosh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_acosh_impl>(cx, argc, vp);
 }
 
 double js::math_asinh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::asinh(x);
 }
 
-bool js::math_asinh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_asinh_impl>(cx, argc, vp);
 }
 
 double js::math_atanh_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::atanh(x);
 }
 
-bool js::math_atanh(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_atanh_impl>(cx, argc, vp);
 }
 
 double js::ecmaHypot(double x, double y) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::hypot(x, y);
 }
 
@@ -825,17 +781,17 @@ double js::hypot4(double x, double y, do
   return scale * std::sqrt(sumsq);
 }
 
 double js::hypot3(double x, double y, double z) {
   AutoUnsafeCallWithABI unsafe;
   return hypot4(x, y, z, 0.0);
 }
 
-bool js::math_hypot(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return math_hypot_handle(cx, args, args.rval());
 }
 
 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
                            MutableHandleValue res) {
   // IonMonkey calls the ecmaHypot function directly if two arguments are
   // given. Do that here as well to get the same results.
@@ -886,72 +842,64 @@ double js::math_trunc_impl(double x) {
   return fdlibm::trunc(x);
 }
 
 float js::math_truncf_impl(float x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::truncf(x);
 }
 
-bool js::math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
-  double x;
-  if (!ToNumber(cx, v, &x)) {
-    return false;
-  }
-
-  r.setNumber(math_trunc_impl(x));
-  return true;
-}
-
 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_trunc_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_trunc_impl(x));
+  return true;
 }
 
 double js::math_sign_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
 
   if (mozilla::IsNaN(x)) {
     return GenericNaN();
   }
 
   return x == 0 ? x : x < 0 ? -1 : 1;
 }
 
-bool js::math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
-  double x;
-  if (!ToNumber(cx, v, &x)) {
-    return false;
-  }
-
-  r.setNumber(math_sign_impl(x));
-  return true;
-}
-
-bool js::math_sign(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_sign(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
-  return math_sign_handle(cx, args[0], args.rval());
+  double x;
+  if (!ToNumber(cx, args[0], &x)) {
+    return false;
+  }
+
+  args.rval().setNumber(math_sign_impl(x));
+  return true;
 }
 
 double js::math_cbrt_impl(double x) {
   AutoUnsafeCallWithABI unsafe;
   return fdlibm::cbrt(x);
 }
 
-bool js::math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
+static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
   return math_function<math_cbrt_impl>(cx, argc, vp);
 }
 
 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setString(cx->names().Math);
   return true;
 }
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -56,172 +56,86 @@ extern const JSClass MathClass;
 extern uint64_t GenerateRandomSeed();
 
 // Fill |seed[0]| and |seed[1]| with random bits, suitable for
 // seeding a XorShift128+ random number generator.
 extern void GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
 
 extern double math_random_impl(JSContext* cx);
 
-extern bool math_random(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_abs_handle(JSContext* cx, js::HandleValue v,
-                            js::MutableHandleValue r);
+extern double math_abs_impl(double x);
 
 extern bool math_abs(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern double math_max_impl(double x, double y);
 
 extern bool math_max(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern double math_min_impl(double x, double y);
 
 extern bool math_min(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern double math_sqrt_impl(double x);
 
-extern bool math_sqrt_handle(JSContext* cx, js::HandleValue number,
-                             js::MutableHandleValue result);
-
-extern bool math_sqrt(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_pow(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool minmax_impl(JSContext* cx, bool max, js::HandleValue a,
-                        js::HandleValue b, js::MutableHandleValue res);
-
 extern bool math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
                              MutableHandleValue res);
 
-extern bool math_imul(JSContext* cx, unsigned argc, js::Value* vp);
-
 extern bool RoundFloat32(JSContext* cx, HandleValue v, float* out);
 
 extern bool RoundFloat32(JSContext* cx, HandleValue arg,
                          MutableHandleValue res);
 
-extern bool math_fround(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_log(JSContext* cx, unsigned argc, js::Value* vp);
+extern double RoundFloat32(double d);
 
 extern double math_log_impl(double x);
 
-extern bool math_log_handle(JSContext* cx, HandleValue val,
-                            MutableHandleValue res);
-
 extern bool math_use_fdlibm_for_sin_cos_tan();
 
-extern bool math_sin(JSContext* cx, unsigned argc, js::Value* vp);
-
 extern double math_sin_fdlibm_impl(double x);
 extern double math_sin_native_impl(double x);
-
-extern bool math_sin_handle(JSContext* cx, HandleValue val,
-                            MutableHandleValue res);
-
-extern bool math_cos(JSContext* cx, unsigned argc, js::Value* vp);
+extern double math_sin_impl(double x);
 
 extern double math_cos_fdlibm_impl(double x);
 extern double math_cos_native_impl(double x);
-
-extern bool math_exp(JSContext* cx, unsigned argc, js::Value* vp);
+extern double math_cos_impl(double x);
 
 extern double math_exp_impl(double x);
 
-extern bool math_tan(JSContext* cx, unsigned argc, js::Value* vp);
-
 extern double math_tan_fdlibm_impl(double x);
 extern double math_tan_native_impl(double x);
-
-extern bool math_log10(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_log2(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_log1p(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_expm1(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_cosh(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_sinh(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_tanh(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_acosh(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_asinh(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_atanh(JSContext* cx, unsigned argc, js::Value* vp);
+extern double math_tan_impl(double x);
 
 extern double ecmaHypot(double x, double y);
 
 extern double hypot3(double x, double y, double z);
 
 extern double hypot4(double x, double y, double z, double w);
 
-extern bool math_hypot(JSContext* cx, unsigned argc, Value* vp);
-
 extern bool math_hypot_handle(JSContext* cx, HandleValueArray args,
                               MutableHandleValue res);
 
 extern bool math_trunc(JSContext* cx, unsigned argc, Value* vp);
 
-extern bool math_sign(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_cbrt(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_asin(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_acos(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_atan(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x,
-                              MutableHandleValue res);
-
-extern bool math_atan2(JSContext* cx, unsigned argc, Value* vp);
-
 extern double ecmaAtan2(double x, double y);
 
 extern double math_atan_impl(double x);
 
-extern bool math_atan(JSContext* cx, unsigned argc, js::Value* vp);
-
 extern double math_asin_impl(double x);
 
-extern bool math_asin(JSContext* cx, unsigned argc, js::Value* vp);
-
 extern double math_acos_impl(double x);
 
-extern bool math_acos(JSContext* cx, unsigned argc, js::Value* vp);
-
-extern bool math_ceil_handle(JSContext* cx, HandleValue value,
-                             MutableHandleValue res);
-
-extern bool math_ceil(JSContext* cx, unsigned argc, Value* vp);
-
 extern double math_ceil_impl(double x);
 
-extern bool math_clz32(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool math_floor_handle(JSContext* cx, HandleValue v,
-                              MutableHandleValue r);
-
 extern bool math_floor(JSContext* cx, unsigned argc, Value* vp);
 
 extern double math_floor_impl(double x);
 
 template <typename T>
 extern T GetBiggestNumberLessThan(T x);
 
-extern bool math_round_handle(JSContext* cx, HandleValue arg,
-                              MutableHandleValue res);
-
-extern bool math_round(JSContext* cx, unsigned argc, Value* vp);
-
 extern double math_round_impl(double x);
 
 extern float math_roundf_impl(float x);
 
 extern double powi(double x, int32_t y);
 
 extern double ecmaPow(double x, double y);
 
@@ -244,21 +158,15 @@ extern double math_acosh_impl(double x);
 extern double math_asinh_impl(double x);
 
 extern double math_atanh_impl(double x);
 
 extern double math_trunc_impl(double x);
 
 extern float math_truncf_impl(float x);
 
-extern bool math_trunc_handle(JSContext* cx, HandleValue v,
-                              MutableHandleValue r);
-
 extern double math_sign_impl(double x);
 
-extern bool math_sign_handle(JSContext* cx, HandleValue v,
-                             MutableHandleValue r);
-
 extern double math_cbrt_impl(double x);
 
 } /* namespace js */
 
 #endif /* jsmath_h */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5605,17 +5605,18 @@ static bool FrontendTest(JSContext* cx, 
             return false;
           }
 
           MainThreadErrorContext ec(cx);
           Rooted<frontend::CompilationInput> input(
               cx, frontend::CompilationInput(options));
           UniquePtr<frontend::ExtensibleCompilationStencil> stencil;
           if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(
-                  cx, &ec, input.get(), srcBuf, stencil)) {
+                  cx, &ec, cx->stackLimitForCurrentPrincipal(), input.get(),
+                  srcBuf, stencil)) {
             return false;
           }
           if (!stencil) {
             JS_ReportErrorASCII(cx, "SmooshMonkey failed to parse");
             return false;
           }
 
 #  ifdef DEBUG
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1236,16 +1236,19 @@ size_t JSContext::sizeOfIncludingThis(
 
 #ifdef DEBUG
 bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
 #endif
 
 void JSContext::trace(JSTracer* trc) {
   cycleDetectorVector().trace(trc);
   geckoProfiler().trace(trc);
+  if (isolate) {
+    irregexp::TraceIsolate(trc, isolate.ref());
+  }
 }
 
 JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) {
 #ifdef JS_SIMULATOR
   return simulator()->stackLimit();
 #else
   return stackLimit(kind);
 #endif
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -206,17 +206,18 @@ RegExpObject* RegExpObject::createSyntax
 
 RegExpObject* RegExpObject::create(JSContext* cx, Handle<JSAtom*> source,
                                    RegExpFlags flags, NewObjectKind newKind) {
   MainThreadErrorContext ec(cx);
   CompileOptions dummyOptions(cx);
   frontend::DummyTokenStream dummyTokenStream(cx, &ec, dummyOptions);
 
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
-  if (!irregexp::CheckPatternSyntax(cx, dummyTokenStream, source, flags)) {
+  if (!irregexp::CheckPatternSyntax(cx, cx->stackLimitForCurrentPrincipal(),
+                                    dummyTokenStream, source, flags)) {
     return nullptr;
   }
 
   Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
   if (!regexp) {
     return nullptr;
   }
 
@@ -1197,18 +1198,18 @@ JS_PUBLIC_API bool JS::CheckRegExpSyntax
 
   MainThreadErrorContext ec(cx);
   CompileOptions dummyOptions(cx);
   frontend::DummyTokenStream dummyTokenStream(cx, &ec, dummyOptions);
 
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
 
   mozilla::Range<const char16_t> source(chars, length);
-  bool success =
-      irregexp::CheckPatternSyntax(cx, dummyTokenStream, source, flags);
+  bool success = irregexp::CheckPatternSyntax(
+      cx, cx->stackLimitForCurrentPrincipal(), dummyTokenStream, source, flags);
   error.set(UndefinedValue());
   if (!success) {
     // We can fail because of OOM or over-recursion even if the syntax is valid.
     if (cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed()) {
       return false;
     }
     if (!cx->getPendingException(error)) {
       return false;
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -34,17 +34,19 @@
 
 #include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "frontend/ParserAtom.h"     // ParserAtomsTable, TaggedParserAtomIndex
 #include "frontend/SharedContext.h"  // TopLevelFunction
 #include "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
 #include "gc/Policy.h"
-#include "js/BuildId.h"               // JS::BuildIdCharVector
+#include "jit/InlinableNatives.h"
+#include "js/BuildId.h"  // JS::BuildIdCharVector
+#include "js/experimental/JitInfo.h"
 #include "js/friend/ErrorMessages.h"  // JSMSG_*
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/ScalarType.h"  // js::Scalar::Type
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/DifferentialTesting.h"
@@ -6661,90 +6663,78 @@ static bool ValidateArrayView(JSContext*
   bool tac = IsTypedArrayConstructor(v, global.viewType());
   if (!tac) {
     return LinkFail(cx, "bad typed array constructor");
   }
 
   return true;
 }
 
+static InlinableNative ToInlinableNative(AsmJSMathBuiltinFunction func) {
+  switch (func) {
+    case AsmJSMathBuiltin_sin:
+      return InlinableNative::MathSin;
+    case AsmJSMathBuiltin_cos:
+      return InlinableNative::MathCos;
+    case AsmJSMathBuiltin_tan:
+      return InlinableNative::MathTan;
+    case AsmJSMathBuiltin_asin:
+      return InlinableNative::MathASin;
+    case AsmJSMathBuiltin_acos:
+      return InlinableNative::MathACos;
+    case AsmJSMathBuiltin_atan:
+      return InlinableNative::MathATan;
+    case AsmJSMathBuiltin_ceil:
+      return InlinableNative::MathCeil;
+    case AsmJSMathBuiltin_floor:
+      return InlinableNative::MathFloor;
+    case AsmJSMathBuiltin_exp:
+      return InlinableNative::MathExp;
+    case AsmJSMathBuiltin_log:
+      return InlinableNative::MathLog;
+    case AsmJSMathBuiltin_pow:
+      return InlinableNative::MathPow;
+    case AsmJSMathBuiltin_sqrt:
+      return InlinableNative::MathSqrt;
+    case AsmJSMathBuiltin_abs:
+      return InlinableNative::MathAbs;
+    case AsmJSMathBuiltin_atan2:
+      return InlinableNative::MathATan2;
+    case AsmJSMathBuiltin_imul:
+      return InlinableNative::MathImul;
+    case AsmJSMathBuiltin_fround:
+      return InlinableNative::MathFRound;
+    case AsmJSMathBuiltin_min:
+      return InlinableNative::MathMin;
+    case AsmJSMathBuiltin_max:
+      return InlinableNative::MathMax;
+    case AsmJSMathBuiltin_clz32:
+      return InlinableNative::MathClz32;
+  }
+  MOZ_CRASH("Invalid asm.js math builtin function");
+}
+
 static bool ValidateMathBuiltinFunction(JSContext* cx,
                                         const AsmJSGlobal& global,
                                         HandleValue globalVal) {
   RootedValue v(cx);
   if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) {
     return false;
   }
 
   if (!GetDataProperty(cx, v, global.field(), &v)) {
     return false;
   }
 
-  Native native = nullptr;
-  switch (global.mathBuiltinFunction()) {
-    case AsmJSMathBuiltin_sin:
-      native = math_sin;
-      break;
-    case AsmJSMathBuiltin_cos:
-      native = math_cos;
-      break;
-    case AsmJSMathBuiltin_tan:
-      native = math_tan;
-      break;
-    case AsmJSMathBuiltin_asin:
-      native = math_asin;
-      break;
-    case AsmJSMathBuiltin_acos:
-      native = math_acos;
-      break;
-    case AsmJSMathBuiltin_atan:
-      native = math_atan;
-      break;
-    case AsmJSMathBuiltin_ceil:
-      native = math_ceil;
-      break;
-    case AsmJSMathBuiltin_floor:
-      native = math_floor;
-      break;
-    case AsmJSMathBuiltin_exp:
-      native = math_exp;
-      break;
-    case AsmJSMathBuiltin_log:
-      native = math_log;
-      break;
-    case AsmJSMathBuiltin_pow:
-      native = math_pow;
-      break;
-    case AsmJSMathBuiltin_sqrt:
-      native = math_sqrt;
-      break;
-    case AsmJSMathBuiltin_min:
-      native = math_min;
-      break;
-    case AsmJSMathBuiltin_max:
-      native = math_max;
-      break;
-    case AsmJSMathBuiltin_abs:
-      native = math_abs;
-      break;
-    case AsmJSMathBuiltin_atan2:
-      native = math_atan2;
-      break;
-    case AsmJSMathBuiltin_imul:
-      native = math_imul;
-      break;
-    case AsmJSMathBuiltin_clz32:
-      native = math_clz32;
-      break;
-    case AsmJSMathBuiltin_fround:
-      native = math_fround;
-      break;
-  }
-
-  if (!IsNativeFunction(v, native)) {
+  InlinableNative native = ToInlinableNative(global.mathBuiltinFunction());
+
+  JSFunction* fun;
+  if (!IsFunctionObject(v, &fun) || !fun->hasJitInfo() ||
+      fun->jitInfo()->type() != JSJitInfo::InlinableNative ||
+      fun->jitInfo()->inlinableNative != native) {
     return LinkFail(cx, "bad Math.* builtin function");
   }
 
   return true;
 }
 
 static bool ValidateConstant(JSContext* cx, const AsmJSGlobal& global,
                              HandleValue globalVal) {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/editing/crashtests/indent-outdent-after-closing-editable-dialog-element.html
@@ -0,0 +1,31 @@
+<html class="reftest-wait">
+<script>
+var eventCount = 0;
+document.addEventListener("DOMContentLoaded", () => {
+  const dialog = document.querySelector("dialog");
+  const object = document.createElement("object");
+  object.addEventListener("DOMSubtreeModified", () => {
+    dialog.show();
+    dialog.focus();
+    document.execCommand("selectAll");
+    dialog.close();
+    setTimeout(() => {
+      document.execCommand("selectAll");
+      document.execCommand("strikeThrough");
+      document.execCommand("indent");
+      document.execCommand("outdent");
+      eventCount--;
+      if (!eventCount) {
+        document.documentElement.removeAttribute("class");
+      }
+    });
+    eventCount++;
+  });
+  object.setAttribute("role", "x"); // Run DOMSubtreeModified
+  object.setAttribute("role", "y"); // Run DOMSubtreeModified
+  document.execCommand("forwardDelete");
+  document.execCommand("justifyRight");
+})
+</script>
+<dialog id="a" contenteditable="true">a</dialog>
+</html>
\ No newline at end of file
--- a/testing/web-platform/tests/lint.ignore
+++ b/testing/web-platform/tests/lint.ignore
@@ -830,14 +830,15 @@ SET TIMEOUT: mediacapture-insertable-str
 
 CSS-COLLIDING-REF-NAME: css/css-backgrounds/reference/background-image-001-ref.html
 CSS-COLLIDING-REF-NAME: css/css-break/background-image-001-ref.html
 
 # Ported crashtests from Mozilla
 SET TIMEOUT: editing/crashtests/backcolor-in-nested-editing-host-td-from-DOMAttrModified.html
 SET TIMEOUT: editing/crashtests/contenteditable-will-be-blurred-by-focus-event-listener.html
 SET TIMEOUT: editing/crashtests/designMode-document-will-be-blurred-by-focus-event-listener.html
+SET TIMEOUT: editing/crashtests/indent-outdent-after-closing-editable-dialog-element.html
 SET TIMEOUT: editing/crashtests/inserthtml-after-temporarily-removing-document-element.html
 SET TIMEOUT: editing/crashtests/inserthtml-in-text-adopted-to-other-document.html
 SET TIMEOUT: editing/crashtests/insertorderedlist-in-text-adopted-to-other-document.html
 SET TIMEOUT: editing/crashtests/make-editable-div-inline-and-set-contenteditable-of-input-to-false.html
 SET TIMEOUT: editing/crashtests/outdent-across-svg-boundary.html
 SET TIMEOUT: editing/crashtests/textarea-will-be-blurred-by-focus-event-listener.html
--- a/toolkit/modules/tests/xpcshell/test_BrowserUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_BrowserUtils.js
@@ -183,16 +183,17 @@ add_task(async function test_shouldShowF
   // Don't show when promo disabled by pref
   Preferences.set("browser.promo.focus.enabled", false);
   Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.FOCUS));
 
   Preferences.resetBranch("browser.promo.focus");
 });
 
 add_task(async function test_shouldShowPinPromo() {
+  Preferences.set("browser.promo.pin.enabled", true);
   // Show pin promo type by default when promo is enabled
   Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN));
 
   // Don't show when there is an enterprise policy active
   if (AppConstants.platform !== "android") {
     // Services.policies isn't shipped on Android
     await setupEnterprisePolicy();
 
--- a/widget/tests/browser/browser_test_swipe_gesture.js
+++ b/widget/tests/browser/browser_test_swipe_gesture.js
@@ -130,16 +130,18 @@ async function panLeftToRightEnd(aElemen
     aX,
     aY,
     0,
     0,
     NativePanHandler.endPhase
   );
 }
 
+requestLongerTimeout(2);
+
 add_task(async () => {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
       ["browser.gesture.swipe.eight", "Browser:ForwardOrForwardDuplicate"],
       ["widget.disable-swipe-tracker", false],
       ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
       ["widget.swipe.success-velocity-contribution", 0.5],
@@ -821,8 +823,76 @@ add_task(async () => {
     return (
       gHistorySwipeAnimation._prevBox == null &&
       gHistorySwipeAnimation._nextBox == null
     );
   });
 
   BrowserTestUtils.removeTab(tab);
 });
+
+// A simple test case on RTL.
+add_task(async () => {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+      ["browser.gesture.swipe.eight", "Browser:ForwardOrForwardDuplicate"],
+      ["widget.disable-swipe-tracker", false],
+      ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+      ["widget.swipe.success-velocity-contribution", 0.5],
+      ["intl.l10n.pseudo", "bidi"],
+    ],
+  });
+
+  const newWin = await BrowserTestUtils.openNewBrowserWindow();
+
+  const firstPage = "about:about";
+  const secondPage = "about:mozilla";
+  const tab = await BrowserTestUtils.openNewForegroundTab(
+    newWin.gBrowser,
+    firstPage,
+    true /* waitForLoad */
+  );
+
+  BrowserTestUtils.loadURI(tab.linkedBrowser, secondPage);
+  await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+  // Make sure we can go back to the previous page.
+  ok(newWin.gBrowser.webNavigation.canGoBack);
+  // and we cannot go forward to the next page.
+  ok(!newWin.gBrowser.webNavigation.canGoForward);
+
+  // Make sure that our gesture support stuff has been initialized in the new
+  // browser window.
+  await TestUtils.waitForCondition(() => {
+    return newWin.gHistorySwipeAnimation.active;
+  });
+
+  // Try to navigate backward.
+  let startLoadingPromise = BrowserTestUtils.browserStarted(
+    tab.linkedBrowser,
+    firstPage
+  );
+  let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+    tab.linkedBrowser,
+    firstPage
+  );
+  await panRightToLeft(tab.linkedBrowser, 100, 100, 1);
+  await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+  ok(newWin.gBrowser.webNavigation.canGoForward);
+
+  // Now try to navigate forward again.
+  startLoadingPromise = BrowserTestUtils.browserStarted(
+    tab.linkedBrowser,
+    secondPage
+  );
+  stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+    tab.linkedBrowser,
+    secondPage
+  );
+  await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+  await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+  ok(newWin.gBrowser.webNavigation.canGoBack);
+
+  await BrowserTestUtils.closeWindow(newWin);
+});