Merge autoland to mozilla-central. a=merge
authorIulian Moraru <imoraru@mozilla.com>
Tue, 05 Jul 2022 12:54:55 +0300
changeset 622958 b985f83ebfec860d9d37b33268c81c4ed1c9af57
parent 622949 a4f0792ffbd86bc56d885ec3509dcd24ac085a6e (current diff)
parent 622957 c3875c2b32465dceb548dc814065d81d7944c9a2 (diff)
child 623033 e979c0bb36c46e885d7b6d6b3dddc5707f80f0e2
push id39937
push userimoraru@mozilla.com
push dateTue, 05 Jul 2022 09:59:04 +0000
treeherdermozilla-central@b985f83ebfec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone104.0a1
first release with
nightly linux32
b985f83ebfec / 104.0a1 / 20220705095904 / files
nightly linux64
b985f83ebfec / 104.0a1 / 20220705095904 / files
nightly mac
b985f83ebfec / 104.0a1 / 20220705095904 / files
nightly win32
b985f83ebfec / 104.0a1 / 20220705095904 / files
nightly win64
b985f83ebfec / 104.0a1 / 20220705095904 / 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/components/migration/ChromeProfileMigrator.jsm
+++ b/browser/components/migration/ChromeProfileMigrator.jsm
@@ -14,24 +14,23 @@ const AUTH_TYPE = {
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { AppConstants } = ChromeUtils.import(
   "resource://gre/modules/AppConstants.jsm"
 );
-const { MigratorPrototype } = ChromeUtils.import(
+const { MigratorPrototype, MigrationUtils } = ChromeUtils.import(
   "resource:///modules/MigrationUtils.jsm"
 );
 
 const lazy = {};
 XPCOMUtils.defineLazyModuleGetters(lazy, {
   ChromeMigrationUtils: "resource:///modules/ChromeMigrationUtils.jsm",
-  MigrationUtils: "resource:///modules/MigrationUtils.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   Qihoo360seMigrationUtils: "resource:///modules/360seMigrationUtils.jsm",
 });
 
 /**
@@ -231,17 +230,17 @@ async function GetBookmarksResource(aPro
     bookmarksPath = alternativeBookmarks.path;
   }
 
   if (!(await lazy.OS.File.exists(bookmarksPath))) {
     return null;
   }
 
   return {
-    type: lazy.MigrationUtils.resourceTypes.BOOKMARKS,
+    type: MigrationUtils.resourceTypes.BOOKMARKS,
 
     migrate(aCallback) {
       return (async function() {
         let gotErrors = false;
         let errorGatherer = function() {
           gotErrors = true;
         };
         // Parse Chrome bookmark file that is JSON format
@@ -253,29 +252,29 @@ async function GetBookmarksResource(aPro
         // Importing bookmark bar items
         if (roots.bookmark_bar.children && roots.bookmark_bar.children.length) {
           // Toolbar
           let parentGuid = lazy.PlacesUtils.bookmarks.toolbarGuid;
           let bookmarks = convertBookmarks(
             roots.bookmark_bar.children,
             errorGatherer
           );
-          await lazy.MigrationUtils.insertManyBookmarksWrapper(
+          await MigrationUtils.insertManyBookmarksWrapper(
             bookmarks,
             parentGuid
           );
           lazy.PlacesUIUtils.maybeToggleBookmarkToolbarVisibilityAfterMigration();
         }
 
         // Importing bookmark menu items
         if (roots.other.children && roots.other.children.length) {
           // Bookmark menu
           let parentGuid = lazy.PlacesUtils.bookmarks.menuGuid;
           let bookmarks = convertBookmarks(roots.other.children, errorGatherer);
-          await lazy.MigrationUtils.insertManyBookmarksWrapper(
+          await MigrationUtils.insertManyBookmarksWrapper(
             bookmarks,
             parentGuid
           );
         }
         if (gotErrors) {
           throw new Error("The migration included errors.");
         }
       })().then(
@@ -288,17 +287,17 @@ async function GetBookmarksResource(aPro
 
 async function GetHistoryResource(aProfileFolder) {
   let historyPath = lazy.OS.Path.join(aProfileFolder, "History");
   if (!(await lazy.OS.File.exists(historyPath))) {
     return null;
   }
 
   return {
-    type: lazy.MigrationUtils.resourceTypes.HISTORY,
+    type: MigrationUtils.resourceTypes.HISTORY,
 
     migrate(aCallback) {
       (async function() {
         const MAX_AGE_IN_DAYS = Services.prefs.getIntPref(
           "browser.migrate.chrome.history.maxAgeInDays"
         );
         const LIMIT = Services.prefs.getIntPref(
           "browser.migrate.chrome.history.limit"
@@ -311,17 +310,17 @@ async function GetHistoryResource(aProfi
             Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000
           );
           query += " AND last_visit_time > " + maxAge;
         }
         if (LIMIT) {
           query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT;
         }
 
-        let rows = await lazy.MigrationUtils.getRowsFromDBWithoutLocks(
+        let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
           historyPath,
           "Chrome history",
           query
         );
         let pageInfos = [];
         let fallbackVisitDate = new Date();
         for (let row of rows) {
           try {
@@ -345,17 +344,17 @@ async function GetHistoryResource(aProfi
               ],
             });
           } catch (e) {
             Cu.reportError(e);
           }
         }
 
         if (pageInfos.length) {
-          await lazy.MigrationUtils.insertVisitsWrapper(pageInfos);
+          await MigrationUtils.insertVisitsWrapper(pageInfos);
         }
       })().then(
         () => {
           aCallback(true);
         },
         ex => {
           Cu.reportError(ex);
           aCallback(false);
@@ -367,21 +366,21 @@ async function GetHistoryResource(aProfi
 
 async function GetCookiesResource(aProfileFolder) {
   let cookiesPath = lazy.OS.Path.join(aProfileFolder, "Cookies");
   if (!(await lazy.OS.File.exists(cookiesPath))) {
     return null;
   }
 
   return {
-    type: lazy.MigrationUtils.resourceTypes.COOKIES,
+    type: MigrationUtils.resourceTypes.COOKIES,
 
     async migrate(aCallback) {
       // Get columns names and set is_sceure, is_httponly fields accordingly.
-      let columns = await lazy.MigrationUtils.getRowsFromDBWithoutLocks(
+      let columns = await MigrationUtils.getRowsFromDBWithoutLocks(
         cookiesPath,
         "Chrome cookies",
         `PRAGMA table_info(cookies)`
       ).catch(ex => {
         Cu.reportError(ex);
         aCallback(false);
       });
       // If the promise was rejected we will have already called aCallback,
@@ -395,17 +394,17 @@ async function GetCookiesResource(aProfi
         : "httponly";
       let isSecure = columns.includes("is_secure") ? "is_secure" : "secure";
 
       let source_scheme = columns.includes("source_scheme")
         ? "source_scheme"
         : `"${Ci.nsICookie.SCHEME_UNSET}" as source_scheme`;
 
       // We don't support decrypting cookies yet so only import plaintext ones.
-      let rows = await lazy.MigrationUtils.getRowsFromDBWithoutLocks(
+      let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
         cookiesPath,
         "Chrome cookies",
         `SELECT host_key, name, value, path, expires_utc, ${isSecure}, ${isHttponly}, encrypted_value, ${source_scheme}
         FROM cookies
         WHERE length(encrypted_value) = 0`
       ).catch(ex => {
         Cu.reportError(ex);
         aCallback(false);
@@ -479,20 +478,20 @@ ChromeProfileMigrator.prototype._GetPass
   let {
     _chromeUserDataPathSuffix,
     _keychainServiceName,
     _keychainAccountName,
     _keychainMockPassphrase = null,
   } = this;
 
   return {
-    type: lazy.MigrationUtils.resourceTypes.PASSWORDS,
+    type: MigrationUtils.resourceTypes.PASSWORDS,
 
     async migrate(aCallback) {
-      let rows = await lazy.MigrationUtils.getRowsFromDBWithoutLocks(
+      let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
         loginPath,
         "Chrome passwords",
         `SELECT origin_url, action_url, username_element, username_value,
         password_element, password_value, signon_realm, scheme, date_created,
         times_used FROM logins WHERE blacklisted_by_user = 0`
       ).catch(ex => {
         Cu.reportError(ex);
         aCallback(false);
@@ -598,17 +597,17 @@ ChromeProfileMigrator.prototype._GetPass
           }
           logins.push(loginInfo);
         } catch (e) {
           Cu.reportError(e);
         }
       }
       try {
         if (logins.length) {
-          await lazy.MigrationUtils.insertLoginsWrapper(logins);
+          await MigrationUtils.insertLoginsWrapper(logins);
         }
       } catch (e) {
         Cu.reportError(e);
       }
       if (crypto.finalize) {
         crypto.finalize();
       }
       aCallback(true);
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_css-property_issue.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_css-property_issue.js
@@ -6,33 +6,33 @@
 // Test whether the deprecated CSS property is shown as issue correctly or not.
 
 const { COMPATIBILITY_ISSUE_TYPE } = require("devtools/shared/constants");
 
 const TEST_URI = `
   <style>
   body {
     color: blue;
-    border-block-color: lime;
+    font-variant-alternates: historical-forms;
     user-modify: read-only;
   }
   div {
     ruby-align: center;
   }
   </style>
   <body>
     <div>test</div>
   </body>
 `;
 
 const TEST_DATA_SELECTED = [
   {
     type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY,
-    property: "border-block-color",
-    url: "https://developer.mozilla.org/docs/Web/CSS/border-block-color",
+    property: "font-variant-alternates",
+    url: "https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates",
     deprecated: false,
     experimental: false,
   },
   {
     type: COMPATIBILITY_ISSUE_TYPE.CSS_PROPERTY_ALIASES,
     property: "user-modify",
     url: "https://developer.mozilla.org/docs/Web/CSS/user-modify",
     aliases: ["user-modify"],
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_document-reload.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_document-reload.js
@@ -4,30 +4,30 @@
 "use strict";
 
 // Test the issues after reloading the browsing document.
 
 const TEST_URI = `
   <style>
   body {
     color: blue;
-    border-block-color: lime;
+    ruby-align: center;
     user-modify: read-only;
   }
   div {
     font-variant-alternates: historical-forms;
   }
   </style>
   <body>
     <div>test</div>
   </body>
 `;
 
 const TEST_DATA_SELECTED = [
-  { property: "border-block-color" },
+  { property: "ruby-align" },
   { property: "user-modify" },
 ];
 
 const TEST_DATA_ALL = [
   ...TEST_DATA_SELECTED,
   { property: "font-variant-alternates" },
 ];
 
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_rule-change.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_rule-change.js
@@ -4,48 +4,48 @@
 "use strict";
 
 // Test whether the content of the issue list will be changed when the rules are changed
 // on the rule view.
 
 const TEST_URI = `
   <style>
   .test-class {
-    border-block-color: lime;
+    ruby-align: center;
   }
   div {
     font-variant-alternates: historical-forms;
   }
   </style>
   <div class="test-class">test class</div>
   <div>test</div>
 `;
 
 const TEST_DATA_SELECTED = {
   fullRule: {
     expectedProperties: [
-      { property: "border-block-color" },
+      { property: "ruby-align" },
       { property: "font-variant-alternates" },
     ],
     expectedNodes: [
       {
-        property: "border-block-color",
+        property: "ruby-align",
         nodes: [],
       },
       {
         property: "font-variant-alternates",
         nodes: [],
       },
     ],
   },
   classRule: {
-    expectedProperties: [{ property: "border-block-color" }],
+    expectedProperties: [{ property: "ruby-align" }],
     expectedNodes: [
       {
-        property: "border-block-color",
+        property: "ruby-align",
         nodes: [],
       },
     ],
   },
   elementRule: {
     expectedProperties: [{ property: "font-variant-alternates" }],
     expectedNodes: [
       {
@@ -54,35 +54,35 @@ const TEST_DATA_SELECTED = {
       },
     ],
   },
 };
 
 const TEST_DATA_ALL = {
   fullRule: {
     expectedProperties: [
-      { property: "border-block-color" },
+      { property: "ruby-align" },
       { property: "font-variant-alternates" },
     ],
     expectedNodes: [
       {
-        property: "border-block-color",
+        property: "ruby-align",
         nodes: ["div.test-class"],
       },
       {
         property: "font-variant-alternates",
         nodes: ["div.test-class", "div"],
       },
     ],
   },
   classRule: {
-    expectedProperties: [{ property: "border-block-color" }],
+    expectedProperties: [{ property: "ruby-align" }],
     expectedNodes: [
       {
-        property: "border-block-color",
+        property: "ruby-align",
         nodes: ["div.test-class"],
       },
     ],
   },
   elementRule: {
     expectedProperties: [{ property: "font-variant-alternates" }],
     expectedNodes: [
       {
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_selected-node-change.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_selected-node-change.js
@@ -3,21 +3,21 @@
 
 "use strict";
 
 // Test whether the content of the issue list will be changed when the new node is selected.
 
 const TEST_URI = `
   <style>
   body {
-    border-block-color: lime;
+    ruby-align: center;
   }
 
   .has-issue {
-    border-inline-color: lime;
+    font-variant-alternates: historical-forms;
     user-modify: read-only;
   }
 
   .no-issue {
     color: black;
   }
   </style>
   <body>
@@ -25,33 +25,33 @@ const TEST_URI = `
     <div class="no-issue">no issue</div>
   </body>
 `;
 
 const TEST_DATA_SELECTED = [
   {
     selector: ".has-issue",
     expectedIssues: [
-      { property: "border-inline-color" },
+      { property: "font-variant-alternates" },
       { property: "user-modify" },
     ],
   },
   {
     selector: ".no-issue",
     expectedIssues: [],
   },
   {
     selector: "body",
-    expectedIssues: [{ property: "border-block-color" }],
+    expectedIssues: [{ property: "ruby-align" }],
   },
 ];
 
 const TEST_DATA_ALL = [
-  { property: "border-block-color" },
-  { property: "border-inline-color" },
+  { property: "ruby-align" },
+  { property: "font-variant-alternates" },
   { property: "user-modify" },
 ];
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
 
   const {
     allElementsPane,
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_top-level-target-change.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_event_top-level-target-change.js
@@ -4,29 +4,29 @@
 "use strict";
 
 // Test the issues after navigating to another page.
 
 const TEST_DATA_ISSUES = {
   uri: `
     <style>
     body {
-      border-block-color: lime;
+      ruby-align: center;
     }
     div {
       font-variant-alternates: historical-forms;
     }
     </style>
     <body>
       <div>test</div>
     </body>
   `,
-  expectedIssuesOnSelected: [{ property: "border-block-color" }],
+  expectedIssuesOnSelected: [{ property: "ruby-align" }],
   expectedIssuesOnAll: [
-    { property: "border-block-color" },
+    { property: "ruby-align" },
     { property: "font-variant-alternates" },
   ],
 };
 
 const TEST_DATA_NO_ISSUES = {
   uri: "<body></body>",
   expectedIssuesOnSelected: [],
   expectedIssuesOnAll: [],
--- a/devtools/client/inspector/compatibility/test/browser/browser_compatibility_settings.js
+++ b/devtools/client/inspector/compatibility/test/browser/browser_compatibility_settings.js
@@ -69,24 +69,25 @@ async function updateTargetBrowsers(pane
   const browsers = [
     ...new Set(
       Array.from(panel.querySelectorAll("[data-id]")).map(el =>
         el.getAttribute("data-id")
       )
     ),
   ];
   Assert.deepEqual(
-    browsers,
+    // Filter out IE, to be removed in an upcoming browser compat data sync.
+    // TODO: Remove the filter once D150961 lands. see Bug 1778009
+    browsers.filter(browser => browser != "ie"),
     [
       "chrome",
       "chrome_android",
       "edge",
       "firefox",
       "firefox_android",
-      "ie",
       "safari",
       "safari_ios",
     ],
     "The expected browsers are displayed"
   );
 
   info("Change target browsers");
   const settingsPane = panel.querySelector(".compatibility-settings");
--- a/docs/code-quality/lint/linters/eslint-plugin-mozilla.rst
+++ b/docs/code-quality/lint/linters/eslint-plugin-mozilla.rst
@@ -48,16 +48,17 @@ The plugin implements the following rule
    eslint-plugin-mozilla/prefer-boolean-length-check
    eslint-plugin-mozilla/prefer-formatValues
    eslint-plugin-mozilla/reject-addtask-only
    eslint-plugin-mozilla/reject-chromeutils-import-params
    eslint-plugin-mozilla/reject-eager-module-in-lazy-getter
    eslint-plugin-mozilla/reject-global-this
    eslint-plugin-mozilla/reject-globalThis-modification
    eslint-plugin-mozilla/reject-importGlobalProperties
+   eslint-plugin-mozilla/reject-mixing-eager-and-lazy
    eslint-plugin-mozilla/reject-osfile
    eslint-plugin-mozilla/reject-relative-requires
    eslint-plugin-mozilla/reject-requires-await
    eslint-plugin-mozilla/reject-scriptableunicodeconverter
    eslint-plugin-mozilla/reject-some-requires
    eslint-plugin-mozilla/reject-top-level-await
    eslint-plugin-mozilla/reject-import-system-module-from-non-system
    eslint-plugin-mozilla/use-cc-etc
new file mode 100644
--- /dev/null
+++ b/docs/code-quality/lint/linters/eslint-plugin-mozilla/reject-mixing-eager-and-lazy.rst
@@ -0,0 +1,22 @@
+reject-mixing-eager-and-lazy
+==================================
+
+Rejects defining a lazy getter for a module that's eagerly imported at
+top-level script unconditionally.
+
+Examples of incorrect code for this rule:
+-----------------------------------------
+
+.. code-block:: js
+
+    const { SomeProp } = ChromeUtils.import("resource://gre/modules/Foo.jsm");
+    XPCOMUtils.defineLazyModuleGetter(lazy, {
+      OtherProp: "resource://gre/modules/Foo.jsm",
+    });
+
+Examples of correct code for this rule:
+---------------------------------------
+
+.. code-block:: js
+
+    const { SomeProp, OtherProp } = ChromeUtils.import("resource://gre/modules/Foo.jsm");
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -1511,17 +1511,17 @@ JSObject* BrowsingContext::ReadStructure
   // the decode operation anyway, but we should at least be as safe as possible.
   if (NS_WARN_IF(!NS_IsMainThread())) {
     MOZ_DIAGNOSTIC_ASSERT(false,
                           "We shouldn't be trying to decode a BrowsingContext "
                           "on a background thread.");
     return nullptr;
   }
 
-  JS::RootedValue val(aCx, JS::NullValue());
+  JS::Rooted<JS::Value> val(aCx, JS::NullValue());
   // We'll get rooting hazard errors from the RefPtr destructor if it isn't
   // destroyed before we try to return a raw JSObject*, so create it in its own
   // scope.
   if (RefPtr<BrowsingContext> context = Get(id)) {
     if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
       return nullptr;
     }
   }
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -1943,17 +1943,17 @@ CanonicalBrowsingContext::ChangeRemotene
       change->Cancel(rv);
       return promise.forget();
     }
 
     // Mark prepareToChange as unresolved, and wait for it to become resolved.
     if (blocker && blocker->State() != Promise::PromiseState::Resolved) {
       change->mWaitingForPrepareToChange = true;
       RefPtr<DomPromiseListener> listener = new DomPromiseListener(
-          [change](JSContext* aCx, JS::HandleValue aValue) {
+          [change](JSContext* aCx, JS::Handle<JS::Value> aValue) {
             change->mWaitingForPrepareToChange = false;
             change->MaybeFinish();
           },
           [change](nsresult aRv) { change->Cancel(aRv); });
       blocker->AppendNativeHandler(listener);
     }
   }
 
@@ -2038,17 +2038,17 @@ CanonicalBrowsingContext::ChangeRemotene
   return promise.forget();
 }
 
 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element* aEmbedder) {
   MOZ_DIAGNOSTIC_ASSERT(IsTop());
 
   if (aEmbedder) {
     if (nsCOMPtr<nsIBrowser> browser = aEmbedder->AsBrowser()) {
-      JS::RootedValue key(RootingCx());
+      JS::Rooted<JS::Value> key(RootingCx());
       if (NS_SUCCEEDED(browser->GetPermanentKey(&key)) && key.isObject()) {
         mPermanentKey = key;
       }
     }
   }
 }
 
 MediaController* CanonicalBrowsingContext::GetMediaController() {
@@ -2419,20 +2419,20 @@ nsresult CanonicalBrowsingContext::Write
   }
 
   nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
   AutoJSAPI jsapi;
   if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
     return NS_ERROR_FAILURE;
   }
 
-  JS::RootedValue key(jsapi.cx(), Top()->PermanentKey());
+  JS::Rooted<JS::Value> key(jsapi.cx(), Top()->PermanentKey());
 
   Record<nsCString, Record<nsString, nsString>> storage;
-  JS::RootedValue update(jsapi.cx());
+  JS::Rooted<JS::Value> update(jsapi.cx());
 
   if (!aSesssionStorage.IsEmpty()) {
     SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage,
                                                      storage);
     if (!ToJSValue(jsapi.cx(), storage, &update)) {
       return NS_ERROR_FAILURE;
     }
   } else {
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -165,18 +165,18 @@ NS_IMETHODIMP
 LoadContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
   MOZ_ASSERT(mIsNotNull);
 
   // We shouldn't need this on parent...
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
-LoadContext::GetScriptableOriginAttributes(JSContext* aCx,
-                                           JS::MutableHandleValue aAttrs) {
+LoadContext::GetScriptableOriginAttributes(
+    JSContext* aCx, JS::MutableHandle<JS::Value> aAttrs) {
   bool ok = ToJSValue(aCx, mOriginAttributes, aAttrs);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(void)
 LoadContext::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
   aAttrs = mOriginAttributes;
--- a/docshell/base/timeline/TimelineMarker.cpp
+++ b/docshell/base/timeline/TimelineMarker.cpp
@@ -40,17 +40,17 @@ JSObject* TimelineMarker::GetStack() {
     return mStackTrace;
   }
   return nullptr;
 }
 
 void TimelineMarker::CaptureStack() {
   JSContext* ctx = nsContentUtils::GetCurrentJSContext();
   if (ctx) {
-    JS::RootedObject stack(ctx);
+    JS::Rooted<JSObject*> stack(ctx);
     if (JS::CaptureCurrentStack(ctx, &stack)) {
       mStackTrace.init(ctx, stack.get());
     } else {
       JS_ClearPendingException(ctx);
     }
   }
 }
 
--- a/docshell/shistory/SessionHistoryEntry.cpp
+++ b/docshell/shistory/SessionHistoryEntry.cpp
@@ -1386,27 +1386,28 @@ SessionHistoryEntry::CreateLoadInfo(nsDo
 
 NS_IMETHODIMP
 SessionHistoryEntry::GetBfcacheID(uint64_t* aBfcacheID) {
   *aBfcacheID = SharedInfo()->mId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SessionHistoryEntry::GetWireframe(JSContext* aCx, JS::MutableHandleValue aOut) {
+SessionHistoryEntry::GetWireframe(JSContext* aCx,
+                                  JS::MutableHandle<JS::Value> aOut) {
   if (mWireframe.isNothing()) {
     aOut.set(JS::NullValue());
   } else if (NS_WARN_IF(!mWireframe->ToObjectInternal(aCx, aOut))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SessionHistoryEntry::SetWireframe(JSContext* aCx, JS::HandleValue aArg) {
+SessionHistoryEntry::SetWireframe(JSContext* aCx, JS::Handle<JS::Value> aArg) {
   if (aArg.isNullOrUndefined()) {
     mWireframe = Nothing();
     return NS_OK;
   }
 
   Wireframe wireframe;
   if (aArg.isObject() && wireframe.Init(aCx, aArg)) {
     mWireframe = Some(std::move(wireframe));
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -1102,17 +1102,17 @@ nsSHEntry::AbandonBFCacheEntry() {
 
 NS_IMETHODIMP
 nsSHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
   *aBFCacheID = mShared->GetId();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetWireframe(JSContext* aCx, JS::MutableHandleValue aOut) {
+nsSHEntry::GetWireframe(JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
   aOut.set(JS::NullValue());
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetWireframe(JSContext* aCx, JS::HandleValue aArg) {
+nsSHEntry::SetWireframe(JSContext* aCx, JS::Handle<JS::Value> aArg) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -406,17 +406,17 @@ template <typename T>
   return true;
 }
 
 // Accept records of other things we accept. N.B. This assumes that
 // keys are either UTF-8 or UTF-16-ish. See Bug 1706058.
 template <typename K, typename V>
 [[nodiscard]] bool ToJSValue(JSContext* aCx, const Record<K, V>& aArgument,
                              JS::MutableHandle<JS::Value> aValue) {
-  JS::RootedObject recordObj(aCx, JS_NewPlainObject(aCx));
+  JS::Rooted<JSObject*> recordObj(aCx, JS_NewPlainObject(aCx));
   if (!recordObj) {
     return false;
   }
 
   for (auto& entry : aArgument.Entries()) {
     JS::Rooted<JS::Value> value(aCx);
     if (!ToJSValue(aCx, entry.mValue, &value)) {
       return false;
--- a/dom/indexedDB/FileInfoManager.h
+++ b/dom/indexedDB/FileInfoManager.h
@@ -81,17 +81,17 @@ class FileInfoManager : public FileInfoM
       MOZ_ASSERT(info);
 
       return !info->LockedClearDBRefs(FileInfoManagerGuard{});
     });
 
     return NS_OK;
   }
 
-  class FileInfoManagerGuard {
+  struct FileInfoManagerGuard {
     FileInfoManagerGuard() = default;
   };
 
  private:
   // Runs the given aFileInfoTableOp operation, which must return a FileInfo*,
   // under the FileManager lock, acquires a strong reference to the returned
   // object under the lock, and returns the strong reference.
   template <typename FileInfoTableOp>
--- a/remote/shared/messagehandler/WindowGlobalMessageHandler.jsm
+++ b/remote/shared/messagehandler/WindowGlobalMessageHandler.jsm
@@ -1,31 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EXPORTED_SYMBOLS = ["WindowGlobalMessageHandler"];
 
-const { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
-
-const { MessageHandler } = ChromeUtils.import(
+const { ContextDescriptorType, MessageHandler } = ChromeUtils.import(
   "chrome://remote/content/shared/messagehandler/MessageHandler.jsm"
 );
 
-const lazy = {};
-
-XPCOMUtils.defineLazyModuleGetters(lazy, {
-  ContextDescriptorType:
-    "chrome://remote/content/shared/messagehandler/MessageHandler.jsm",
-});
-
 /**
  * A WindowGlobalMessageHandler is dedicated to debugging a single window
  * global. It follows the lifecycle of the corresponding window global and will
  * therefore not survive any navigation. This MessageHandler cannot forward
  * commands further to other MessageHandlers and represents a leaf node in a
  * MessageHandler network.
  */
 class WindowGlobalMessageHandler extends MessageHandler {
@@ -128,15 +117,14 @@ class WindowGlobalMessageHandler extends
   forwardCommand(command) {
     throw new Error(
       `Cannot forward commands from a "WINDOW_GLOBAL" MessageHandler`
     );
   }
 
   _matchesContext(contextDescriptor) {
     return (
-      contextDescriptor.type === lazy.ContextDescriptorType.All ||
-      (contextDescriptor.type ===
-        lazy.ContextDescriptorType.TopBrowsingContext &&
+      contextDescriptor.type === ContextDescriptorType.All ||
+      (contextDescriptor.type === ContextDescriptorType.TopBrowsingContext &&
         contextDescriptor.id === this._context.browserId)
     );
   }
 }
--- a/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_localization.js
+++ b/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_localization.js
@@ -18,11 +18,16 @@ async function doOne(resource, id) {
 add_task(async function test_localization() {
   // Verify that the `l10n-registry` category is processed and that localization
   // works as expected in background tasks.  We can use any FTL resource and
   // string identifier here as long as the value is short and can be passed as a
   // command line argument safely (i.e., is ASCII).
 
   // One from toolkit/.
   await doOne("toolkit/global/commonDialog.ftl", "common-dialog-title-system");
-  // And one from browser/.
-  await doOne("browser/pageInfo.ftl", "not-set-date");
+  if (AppConstants.MOZ_APP_NAME == "thunderbird") {
+    // And one from messenger/.
+    await doOne("messenger/messenger.ftl", "no-reply-title");
+  } else {
+    // And one from browser/.
+    await doOne("browser/pageInfo.ftl", "not-set-date");
+  }
 });
--- a/toolkit/components/search/OpenSearchEngine.jsm
+++ b/toolkit/components/search/OpenSearchEngine.jsm
@@ -1,26 +1,25 @@
 /* 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/. */
 
 /* eslint no-shadow: error, mozilla/no-aArgs: error */
 
-const { SearchEngine } = ChromeUtils.import(
+const { EngineURL, SearchEngine } = ChromeUtils.import(
   "resource://gre/modules/SearchEngine.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 const lazy = {};
 
 XPCOMUtils.defineLazyModuleGetters(lazy, {
-  EngineURL: "resource://gre/modules/SearchEngine.jsm",
   SearchUtils: "resource://gre/modules/SearchUtils.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(lazy, "logConsole", () => {
   return console.createInstance({
     prefix: "OpenSearchEngine",
     maxLogLevel: lazy.SearchUtils.loggingEnabled ? "Debug" : "Warn",
   });
@@ -281,17 +280,17 @@ class OpenSearchEngine extends SearchEng
     }
 
     // Support an alternate suggestion type, see bug 1425827 for details.
     if (type == "application/json" && rels.includes("suggestions")) {
       type = lazy.SearchUtils.URL_TYPE.SUGGEST_JSON;
     }
 
     try {
-      var url = new lazy.EngineURL(type, method, template);
+      var url = new EngineURL(type, method, template);
     } catch (ex) {
       throw Components.Exception(
         "_parseURL: failed to add " + template + " as a URL",
         Cr.NS_ERROR_FAILURE
       );
     }
 
     if (rels.length) {
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -6,17 +6,17 @@
 /* eslint "no-unused-vars": [2, {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}] */
 /* eslint "semi": [2, "always"] */
 /* eslint "valid-jsdoc": [2, {requireReturn: false}] */
 
 var EXPORTED_SYMBOLS = ["AddonTestUtils", "MockAsyncShutdown"];
 
 const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
 
-const { AddonManager, AddonManagerPrivate } = ChromeUtils.import(
+const { AddonManager, AddonManagerPrivate, AMTelemetry } = ChromeUtils.import(
   "resource://gre/modules/AddonManager.jsm"
 );
 const { AsyncShutdown } = ChromeUtils.import(
   "resource://gre/modules/AsyncShutdown.jsm"
 );
 const { FileUtils } = ChromeUtils.import(
   "resource://gre/modules/FileUtils.jsm"
 );
@@ -28,17 +28,16 @@ const { XPCOMUtils } = ChromeUtils.impor
 const { EventEmitter } = ChromeUtils.import(
   "resource://gre/modules/EventEmitter.jsm"
 );
 const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
 const lazy = {};
 
 XPCOMUtils.defineLazyModuleGetters(lazy, {
-  AMTelemetry: "resource://gre/modules/AddonManager.jsm",
   ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
   getAppInfo: "resource://testing-common/AppInfo.jsm",
   Management: "resource://gre/modules/Extension.jsm",
   ExtensionAddonObserver: "resource://gre/modules/Extension.jsm",
   FileTestUtils: "resource://testing-common/FileTestUtils.jsm",
   MockRegistrar: "resource://testing-common/MockRegistrar.jsm",
   updateAppInfo: "resource://testing-common/AppInfo.jsm",
   XPCShellContentUtils: "resource://testing-common/XPCShellContentUtils.jsm",
@@ -1810,27 +1809,27 @@ var AddonTestUtils = {
 
   // AMTelemetry events helpers.
 
   /**
    * Redefine AMTelemetry.recordEvent to collect the recorded telemetry events and
    * ensure that there are no unexamined events after the test file is exiting.
    */
   hookAMTelemetryEvents() {
-    let originalRecordEvent = lazy.AMTelemetry.recordEvent;
-    lazy.AMTelemetry.recordEvent = event => {
+    let originalRecordEvent = AMTelemetry.recordEvent;
+    AMTelemetry.recordEvent = event => {
       this.collectedTelemetryEvents.push(event);
     };
     this.testScope.registerCleanupFunction(() => {
       this.testScope.Assert.deepEqual(
         [],
         this.collectedTelemetryEvents,
         "No unexamined telemetry events after test is finished"
       );
-      lazy.AMTelemetry.recordEvent = originalRecordEvent;
+      AMTelemetry.recordEvent = originalRecordEvent;
     });
   },
 
   /**
    * Retrive any AMTelemetry event collected and empty the array of the collected events.
    *
    * @returns {Array<Object>}
    *          The array of the collected telemetry data.
--- a/toolkit/mozapps/update/BackgroundTask_backgroundupdate.jsm
+++ b/toolkit/mozapps/update/BackgroundTask_backgroundupdate.jsm
@@ -6,33 +6,33 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "backgroundTaskTimeoutSec",
   "maybeSubmitBackgroundUpdatePing",
   "runBackgroundTask",
 ];
 
-const { EXIT_CODE } = ChromeUtils.import(
+const { BackgroundUpdate } = ChromeUtils.import(
   "resource://gre/modules/BackgroundUpdate.jsm"
-).BackgroundUpdate;
+);
+const { EXIT_CODE } = BackgroundUpdate;
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { AppConstants } = ChromeUtils.import(
   "resource://gre/modules/AppConstants.jsm"
 );
 
 const lazy = {};
 
 XPCOMUtils.defineLazyModuleGetters(lazy, {
   AppUpdater: "resource:///modules/AppUpdater.jsm",
   BackgroundTasksUtils: "resource://gre/modules/BackgroundTasksUtils.jsm",
-  BackgroundUpdate: "resource://gre/modules/BackgroundUpdate.jsm",
   ExtensionUtils: "resource://gre/modules/ExtensionUtils.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetter(
   lazy,
   "UpdateService",
@@ -79,19 +79,19 @@ async function _attemptBackgroundUpdate(
   );
   Cc["@mozilla.org/updates/update-service-stub;1"].createInstance(
     Ci.nsISupports
   );
 
   lazy.log.debug(
     `${SLUG}: checking for preconditions necessary to update this installation`
   );
-  let reasons = await lazy.BackgroundUpdate._reasonsToNotUpdateInstallation();
+  let reasons = await BackgroundUpdate._reasonsToNotUpdateInstallation();
 
-  if (lazy.BackgroundUpdate._force()) {
+  if (BackgroundUpdate._force()) {
     // We want to allow developers and testers to monkey with the system.
     lazy.log.debug(
       `${SLUG}: app.update.background.force=true, ignoring reasons: ${JSON.stringify(
         reasons
       )}`
     );
     reasons = [];
   }
@@ -308,17 +308,17 @@ async function runBackgroundTask() {
     return EXIT_CODE.DEFAULT_PROFILE_CANNOT_BE_READ;
   }
 
   // Now that we have prefs from the default profile, we can configure Firefox-on-Glean.
 
   // Glean has a preinit queue for metric operations that happen before init, so
   // this is safe.  We want to have these metrics set before the first possible
   // time we might send (built-in) pings.
-  await lazy.BackgroundUpdate.recordUpdateEnvironment();
+  await BackgroundUpdate.recordUpdateEnvironment();
 
   // The final leaf is for the benefit of `FileUtils`.  To help debugging, use
   // the `GLEAN_LOG_PINGS` and `GLEAN_DEBUG_VIEW_TAG` environment variables: see
   // https://mozilla.github.io/glean/book/user/debugging/index.html.
   let gleanRoot = lazy.FileUtils.getFile("UpdRootD", [
     "backgroundupdate",
     "datareporting",
     "glean",
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -52,16 +52,17 @@ module.exports = {
       rules: {
         "mozilla/lazy-getter-object-name": "error",
         "mozilla/reject-eager-module-in-lazy-getter": "error",
         "mozilla/reject-global-this": "error",
         "mozilla/reject-globalThis-modification": "error",
         // For all system modules, we expect no properties to need importing,
         // hence reject everything.
         "mozilla/reject-importGlobalProperties": ["error", "everything"],
+        "mozilla/reject-mixing-eager-and-lazy": "error",
         "mozilla/reject-top-level-await": "error",
         // TODO: Bug 1575506 turn `builtinGlobals` on here.
         // We can enable builtinGlobals for jsms due to their scopes.
         "no-redeclare": ["error", { builtinGlobals: false }],
       },
     },
     {
       // Temporarily disable until the proxy-based loader gets landed.
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -55,16 +55,17 @@ module.exports = {
     "prefer-formatValues": require("../lib/rules/prefer-formatValues"),
     "reject-addtask-only": require("../lib/rules/reject-addtask-only"),
     "reject-chromeutils-import-params": require("../lib/rules/reject-chromeutils-import-params"),
     "reject-eager-module-in-lazy-getter": require("../lib/rules/reject-eager-module-in-lazy-getter"),
     "reject-global-this": require("../lib/rules/reject-global-this"),
     "reject-globalThis-modification": require("../lib/rules/reject-globalThis-modification"),
     "reject-import-system-module-from-non-system": require("../lib/rules/reject-import-system-module-from-non-system"),
     "reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"),
+    "reject-mixing-eager-and-lazy": require("../lib/rules/reject-mixing-eager-and-lazy"),
     "reject-osfile": require("../lib/rules/reject-osfile"),
     "reject-scriptableunicodeconverter": require("../lib/rules/reject-scriptableunicodeconverter"),
     "reject-relative-requires": require("../lib/rules/reject-relative-requires"),
     "reject-some-requires": require("../lib/rules/reject-some-requires"),
     "reject-top-level-await": require("../lib/rules/reject-top-level-await"),
     "rejects-requires-await": require("../lib/rules/rejects-requires-await"),
     "use-cc-etc": require("../lib/rules/use-cc-etc"),
     "use-chromeutils-generateqi": require("../lib/rules/use-chromeutils-generateqi"),
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-mixing-eager-and-lazy.js
@@ -0,0 +1,153 @@
+/**
+ * @fileoverview Reject use of lazy getters for modules that's loaded early in
+ *               the startup process and not necessarily be lazy.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+const helpers = require("../helpers");
+
+function isIdentifier(node, id) {
+  return node.type === "Identifier" && node.name === id;
+}
+
+function isString(node) {
+  return node.type === "Literal" && typeof node.value === "string";
+}
+
+function checkMixed(loadedModules, context, node, type, resourceURI) {
+  if (!loadedModules.has(resourceURI)) {
+    loadedModules.set(resourceURI, type);
+  }
+
+  if (loadedModules.get(resourceURI) === type) {
+    return;
+  }
+
+  context.report({
+    node,
+    messageId: "mixedEagerAndLazy",
+    data: { uri: resourceURI },
+  });
+}
+
+module.exports = {
+  meta: {
+    docs: {
+      url:
+        "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-mixed-eager-and-lazy.html",
+    },
+    messages: {
+      mixedEagerAndLazy:
+        'Module "{{uri}}" is loaded eagerly, and should not be used for lazy getter.',
+    },
+    type: "problem",
+  },
+
+  create(context) {
+    const loadedModules = new Map();
+
+    return {
+      ImportDeclaration(node) {
+        const resourceURI = node.source.value;
+        checkMixed(loadedModules, context, node, "eager", resourceURI);
+      },
+      CallExpression(node) {
+        if (node.callee.type !== "MemberExpression") {
+          return;
+        }
+
+        let callerSource;
+        try {
+          callerSource = helpers.getASTSource(node.callee);
+        } catch (e) {
+          return;
+        }
+
+        if (
+          (callerSource === "ChromeUtils.import" ||
+            callerSource === "ChromeUtils.importESModule") &&
+          helpers.getIsTopLevelAndUnconditionallyExecuted(
+            context.getAncestors()
+          )
+        ) {
+          if (node.arguments.length < 1) {
+            return;
+          }
+          const resourceURINode = node.arguments[0];
+          if (!isString(resourceURINode)) {
+            return;
+          }
+          checkMixed(
+            loadedModules,
+            context,
+            node,
+            "eager",
+            resourceURINode.value
+          );
+        }
+
+        if (
+          callerSource === "XPCOMUtils.defineLazyModuleGetter" ||
+          callerSource === "ChromeUtils.defineModuleGetter"
+        ) {
+          if (node.arguments.length < 3) {
+            return;
+          }
+          if (!isIdentifier(node.arguments[0], "lazy")) {
+            return;
+          }
+
+          const resourceURINode = node.arguments[2];
+          if (!isString(resourceURINode)) {
+            return;
+          }
+          checkMixed(
+            loadedModules,
+            context,
+            node,
+            "lazy",
+            resourceURINode.value
+          );
+        } else if (
+          callerSource === "XPCOMUtils.defineLazyModuleGetters" ||
+          callerSource === "ChromeUtils.defineESModuleGetters"
+        ) {
+          if (node.arguments.length < 2) {
+            return;
+          }
+          if (!isIdentifier(node.arguments[0], "lazy")) {
+            return;
+          }
+
+          const obj = node.arguments[1];
+          if (obj.type !== "ObjectExpression") {
+            return;
+          }
+          for (let prop of obj.properties) {
+            if (prop.type !== "Property") {
+              continue;
+            }
+            if (prop.kind !== "init") {
+              continue;
+            }
+            const resourceURINode = prop.value;
+            if (!isString(resourceURINode)) {
+              continue;
+            }
+            checkMixed(
+              loadedModules,
+              context,
+              node,
+              "lazy",
+              resourceURINode.value
+            );
+          }
+        }
+      },
+    };
+  },
+};
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/tests/reject-mixing-eager-and-lazy.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require("../lib/rules/reject-mixing-eager-and-lazy");
+var RuleTester = require("eslint").RuleTester;
+
+const ruleTester = new RuleTester({
+  parserOptions: { ecmaVersion: 13, sourceType: "module" },
+});
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+function invalidCode(code, uri) {
+  return { code, errors: [{ messageId: "mixedEagerAndLazy", data: { uri } }] };
+}
+
+ruleTester.run("reject-mixing-eager-and-lazy", rule, {
+  valid: [
+    `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+`,
+    `
+    ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
+`,
+    `
+    import{ AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+`,
+    `
+    XPCOMUtils.defineLazyModuleGetter(
+      lazy, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+    `
+    ChromeUtils.defineModuleGetter(
+      lazy, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+    `
+    XPCOMUtils.defineLazyModuleGetters(lazy, {
+      AppConstants: "resource://gre/modules/AppConstants.jsm",
+    });
+`,
+    `
+    ChromeUtils.defineESModuleGetters(lazy, {
+      AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
+    });
+`,
+    `
+    if (some_condition) {
+      ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    }
+    XPCOMUtils.defineLazyModuleGetter(
+      lazy, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+    `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    XPCOMUtils.defineLazyModuleGetter(
+      sandbox, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+    `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    XPCOMUtils.defineLazyModuleGetters(sandbox, {
+      AppConstants: "resource://gre/modules/AppConstants.jsm",
+    });
+`,
+  ],
+  invalid: [
+    invalidCode(
+      `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    XPCOMUtils.defineLazyModuleGetter(
+      lazy, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+      "resource://gre/modules/AppConstants.jsm"
+    ),
+    invalidCode(
+      `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    ChromeUtils.defineModuleGetter(
+      lazy, "AppConstants", "resource://gre/modules/AppConstants.jsm"
+    );
+`,
+      "resource://gre/modules/AppConstants.jsm"
+    ),
+    invalidCode(
+      `
+    ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+    XPCOMUtils.defineLazyModuleGetters(lazy, {
+      AppConstants: "resource://gre/modules/AppConstants.jsm",
+    });
+`,
+      "resource://gre/modules/AppConstants.jsm"
+    ),
+    invalidCode(
+      `
+    ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
+    ChromeUtils.defineESModuleGetters(lazy, {
+      AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
+    });
+`,
+      "resource://gre/modules/AppConstants.sys.mjs"
+    ),
+    invalidCode(
+      `
+    import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+    ChromeUtils.defineESModuleGetters(lazy, {
+      AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
+    });
+`,
+      "resource://gre/modules/AppConstants.sys.mjs"
+    ),
+  ],
+});
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -1545,17 +1545,17 @@ void GfxInfoBase::RemoveCollector(GfxInf
   }
   if (sCollectors->IsEmpty()) {
     delete sCollectors;
     sCollectors = nullptr;
   }
 }
 
 static void AppendMonitor(JSContext* aCx, widget::Screen& aScreen,
-                          JS::HandleObject aOutArray, int32_t aIndex) {
+                          JS::Handle<JSObject*> aOutArray, int32_t aIndex) {
   JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
 
   auto screenSize = aScreen.GetRect().Size();
 
   JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
   JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
 
   JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
@@ -1578,34 +1578,35 @@ static void AppendMonitor(JSContext* aCx
       aCx, JS::BooleanValue(aScreen.GetIsPseudoDisplay()));
   JS_SetProperty(aCx, obj, "pseudoDisplay", pseudoDisplay);
 #endif
 
   JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
   JS_SetElement(aCx, aOutArray, aIndex, element);
 }
 
-nsresult GfxInfoBase::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) {
+nsresult GfxInfoBase::FindMonitors(JSContext* aCx,
+                                   JS::Handle<JSObject*> aOutArray) {
   int32_t index = 0;
   auto& sm = ScreenManager::GetSingleton();
   for (auto& screen : sm.CurrentScreenList()) {
     AppendMonitor(aCx, *screen, aOutArray, index++);
   }
 
   if (index == 0) {
     // Ensure we return at least one monitor, this is needed for xpcshell.
     RefPtr<Screen> screen = sm.GetPrimaryScreen();
     AppendMonitor(aCx, *screen, aOutArray, index++);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) {
+GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
   JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
 
   nsresult rv = FindMonitors(aCx, array);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   aResult.setObject(*array);
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -50,17 +50,17 @@ class GfxInfoBase : public nsIGfxInfo,
   // using GfxInfoBase::GetFeatureSuggestedDriverVersion;
   // to import the relevant methods into their namespace.
   NS_IMETHOD GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
                               int32_t* _retval) override;
   NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature,
                                               nsAString& _retval) override;
 
   NS_IMETHOD GetMonitors(JSContext* cx,
-                         JS::MutableHandleValue _retval) override;
+                         JS::MutableHandle<JS::Value> _retval) override;
   NS_IMETHOD GetFailures(nsTArray<int32_t>& indices,
                          nsTArray<nsCString>& failures) override;
   NS_IMETHOD_(void) LogFailure(const nsACString& failure) override;
   NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetActiveCrashGuards(JSContext*,
                                   JS::MutableHandle<JS::Value>) override;
@@ -101,17 +101,17 @@ class GfxInfoBase : public nsIGfxInfo,
   virtual nsString Product() { return u""_ns; }
   virtual nsString Manufacturer() { return u""_ns; }
   virtual uint32_t OperatingSystemVersion() { return 0; }
   virtual uint32_t OperatingSystemBuild() { return 0; }
 
   // Convenience to get the application version
   static const nsCString& GetApplicationVersion();
 
-  virtual nsresult FindMonitors(JSContext* cx, JS::HandleObject array);
+  virtual nsresult FindMonitors(JSContext* cx, JS::Handle<JSObject*> array);
 
   static void SetFeatureStatus(
       nsTArray<mozilla::gfx::GfxInfoFeatureStatus>&& aFS);
 
  protected:
   virtual ~GfxInfoBase();
 
   virtual nsresult GetFeatureStatusImpl(