Low CVSS 4.3 firefox Spoofing 🔧 Commit mapped

Overview

Low
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionThe exception page for the HTTPS-Only feature, displayed when a website is opened via HTTP, lacked an anti-clickjacking delay, potentially allowing an attacker to trick a user into granting an exception and loading a webpage over HTTP.
ComponentDOM
Bug ClassSpoofing
Tracker1955182
Fix commitc8bf04636b0a (firefox) +129/-37
CWECWE-1021
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
CISA KEVNot listed
Creditedhafiizh & kang ali
Disclosed2025-06-24

Background

HTTPS-Only mode
When a navigation to an http:// URL cannot be upgraded, Firefox shows the HTTPS-Only error page (toolkit/components/httpsonlyerror/content/errorpage.html) with a “Continue to HTTP Site” button (id="openInsecure") that, when clicked, calls document.reloadWithHttpsOnlyException() and loads the insecure page.
Anti-clickjacking security delay
security.dialog_enable_delay (default 1000 ms) is the interval after a security UI appears/focuses during which a confirming control must stay inert, so a user cannot be tricked into clicking a button that is placed under their cursor just before it becomes active.
`inert` attribute
An HTML attribute that fully disables an element: it cannot be focused or receive events. The old implementation disabled the button with inert and removed it once after a fixed timeout.
Clickjacking
An attacker frames/overlays or times UI so the victim’s click lands on a sensitive control (CWE-1021, UI redressing) without the victim realizing it.

Root Cause Analysis

The security delay was armed exactly once, in initPage() of errorpage.js: a single setTimeout removed the inert attribute after security.dialog_enable_delay. Because inert makes the element incapable of receiving JS events, the page could not observe focus changes on it, and nothing re-armed the delay if the page lost and regained focus (or was reloaded into focus via the identity pane). An attacker could therefore load the error page, let the one-shot timer expire while the tab is backgrounded or obscured, then surface the page and induce a click, so the “Continue to HTTP Site” button was already active with no fresh delay protecting it.

The patch replaces the one-shot inert scheme with a focus-driven, continuously re-armed delay. The button now carries a class="disabled" (styled via the new button.disabled rule in aboutHttpsOnlyError.css) that only looks disabled but still receives events. onFocus/onBlur handlers and resetClickjackingTimeout()/clearClickjackingTimeout() re-start the timer on every focus gain and disable the button on every blur, so the full delay must elapse after the most recent focus before the button becomes clickable. onOpenInsecureButtonClick now checks whether the target still has the disabled class; if so it stopPropagation()/preventDefault() and resets the timeout instead of proceeding. Crucially, browser-siteIdentity.js adds gBrowser.selectedBrowser.focus() when the HTTPS-Only exception is removed via the identity pane, so a reload that lands on the error page correctly triggers a fresh blur/focus cycle and re-arms the delay.

Key insight
The clickjacking delay was a single fixed timer set at page load and never re-armed on focus changes, so after it elapsed the “Continue to HTTP Site” button stayed permanently clickable and could be presented to the user without any protective delay.

Attack Path

  1. Setup Attacker causes the victim to open an http:// URL that fails to upgrade, rendering the HTTPS-Only error page (inferred: attacker controls or links to the page).
  2. Timer burn-down The page is loaded while backgrounded/obscured or otherwise not the user’s focus; the single setTimeout elapses and inert is removed, leaving the button active.
  3. UI redressing Attacker brings the button into view / under the cursor (e.g. a tab-switch, overlay, or enticing click target) with no new delay armed.
  4. Victim click The victim clicks what is now the already-active “Continue to HTTP Site” button, invoking document.reloadWithHttpsOnlyException().
  5. Primitive An HTTPS-Only exception is granted and the page loads over plaintext HTTP, downgrading the connection without a protected, deliberate user confirmation.

Impact Assessment

Renderer/front-end UI issue, no memory corruption and no code execution. The achieved effect is tricking the user into granting an HTTPS-Only exception and loading a page over HTTP (confidentiality/integrity of that one load), matching the “low” impact and CWE-1021 clickjacking classification. Preconditions: HTTPS-Only mode enabled, a navigation that reaches the error page, and the ability to control focus/visibility timing to present the already-enabled button. No privilege is required beyond serving content to the victim.

Changed Functions

FunctionChangeNotes
if
browser/base/content/browser-siteIdentity.js
modified
if
toolkit/components/httpsonlyerror/content/errorpage.js
modified
if
dom/security/test/https-first/browser_https_telemetry.js
modified
for
dom/security/test/https-only/browser_continue_button_delay.js
modified
add_task
dom/security/test/https-only/browser_continue_button_delay.js
modified
if
toolkit/components/httpsonlyerror/tests/browser/head.js
modified

Files Changed

  • browser/base/content/browser-siteIdentity.js
  • dom/security/test/https-first/browser_https_telemetry.js
  • dom/security/test/https-only/browser_continue_button_delay.js
  • toolkit/components/httpsonlyerror/content/errorpage.html
  • toolkit/components/httpsonlyerror/content/errorpage.js
  • toolkit/components/httpsonlyerror/tests/browser/head.js
  • toolkit/themes/shared/aboutHttpsOnlyError.css

Audit Directions

  • Other click-through security dialogs
    Audit any other in-content warning/confirm UI (add-on install, permission prompts, other error pages) for one-shot vs. focus-re-armed delays; the correct pattern is re-arm on every focus.
  • `inert`-based gating
    Look for other security controls gated by inert that therefore cannot observe focus events and cannot re-arm a delay.
  • Focus restoration paths
    Review code paths that reload content into an error page (identity pane, reload-skip-cache) to ensure focus is set so the delay re-arms, as was needed in browser-siteIdentity.js.
diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index 71d085cc45c..def0375826a 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -617,6 +617,9 @@ var gIdentityHandler = {
       if (this._popupInitialized) {
         PanelMultiView.hidePopup(this._identityPopup);
       }
+      // Ensure the browser is focused again, otherwise we may not trigger the
+      // security delay on a potential error page following this reload.
+      gBrowser.selectedBrowser.focus();
       return;
     }
     // Otherwise we just refresh the interface
diff --git a/toolkit/components/httpsonlyerror/content/errorpage.html b/toolkit/components/httpsonlyerror/content/errorpage.html
index 89e5e3cda37..1cfd46594a0 100644
--- a/toolkit/components/httpsonlyerror/content/errorpage.html
+++ b/toolkit/components/httpsonlyerror/content/errorpage.html
@@ -64,10 +64,12 @@
           class="primary"
           data-l10n-id="about-httpsonly-button-go-back"
         ></button>
+        <!-- This button only looks "disabled", but still allows JS events to
+             propagate for the clickjacking protection. -->
         <button
           id="openInsecure"
           data-l10n-id="about-httpsonly-button-continue-to-site"
-          inert
+          class="disabled"
         ></button>
       </div>
 
diff --git a/toolkit/components/httpsonlyerror/content/errorpage.js b/toolkit/components/httpsonlyerror/content/errorpage.js
index 6b5fc931f48..7c7d5fd5dbd 100644
--- a/toolkit/components/httpsonlyerror/content/errorpage.js
+++ b/toolkit/components/httpsonlyerror/content/errorpage.js
@@ -7,6 +7,8 @@
 "use strict";
 
 const searchParams = new URLSearchParams(document.documentURI.split("?")[1]);
+const clickjackingDelay = RPMGetIntPref("security.dialog_enable_delay", 1000);
+let clickjackingTimeout;
 
 function initPage() {
   if (!searchParams.get("e")) {
@@ -49,10 +51,8 @@ function initPage() {
     .getElementById("goBack")
     .addEventListener("click", onReturnButtonClick);
 
-  const delay = RPMGetIntPref("security.dialog_enable_delay", 1000);
-  setTimeout(() => {
-    document.getElementById("openInsecure").removeAttribute("inert");
-  }, delay);
+  document.addEventListener("blur", onBlur);
+  document.addEventListener("focus", onFocus);
 
   addAutofocus("#goBack", "beforeend");
 
@@ -111,14 +111,33 @@ function openSecureWWWButtonClick() {
   RPMOpenSecureWWWLink();
 }
 
-function onOpenInsecureButtonClick() {
-  document.reloadWithHttpsOnlyException();
+function onOpenInsecureButtonClick(e) {
+  if (e.target.classList.contains("disabled")) {
+    e.stopPropagation();
+    e.preventDefault();
+    resetClickjackingTimeout();
+  } else {
+    document.reloadWithHttpsOnlyException();
+  }
 }
 
 function onReturnButtonClick() {
   RPMSendAsyncMessage("goBack");
 }
 
+/* Focus Events */
+
+function onFocus() {
+  resetClickjackingTimeout();
+}
+
+function onBlur() {
+  clearClickjackingTimeout();
+  if (!document.getElementById("openInsecure").classList.contains("disabled")) {
+    document.getElementById("openInsecure").classList.add("disabled");
+  }
+}
+
 /*  Utils */
 
 function addAutofocus(selector, position = "afterbegin") {
@@ -132,6 +151,23 @@ function addAutofocus(selector, position = "afterbegin") {
   parent.insertAdjacentElement(position, button);
 }
 
+function resetClickjackingTimeout() {
+  if (clickjackingTimeout) {
+    clearTimeout(clickjackingTimeout);
+  }
+  clickjackingTimeout = setTimeout(() => {
+    document.getElementById("openInsecure").classList.remove("disabled");
+    clickjackingTimeout = undefined;
+  }, clickjackingDelay);
+}
+
+function clearClickjackingTimeout() {
+  if (clickjackingTimeout) {
+    clearTimeout(clickjackingTimeout);
+    clickjackingTimeout = undefined;
+  }
+}
+
 /* Initialize Page */
 
 initPage();
diff --git a/toolkit/themes/shared/aboutHttpsOnlyError.css b/toolkit/themes/shared/aboutHttpsOnlyError.css
index 04937d8f45e..e8345aa6c4f 100644
--- a/toolkit/themes/shared/aboutHttpsOnlyError.css
+++ b/toolkit/themes/shared/aboutHttpsOnlyError.css
@@ -86,3 +86,10 @@ ul > li {
     opacity: 1;
   }
 }
+
+/* We need this extra class instead of just adding a "disabled" attribute to the
+ * button, as we still need to receive JS events from it. */
+button.disabled {
+  opacity: var(--button-opacity-disabled);
+  color: var(--button-text-color-disabled);
+}
diff --git a/dom/security/test/https-first/browser_https_telemetry.js b/dom/security/test/https-first/browser_https_telemetry.js
index d952de1c94e..34242590e9e 100644
--- a/dom/security/test/https-first/browser_https_telemetry.js
+++ b/dom/security/test/https-first/browser_https_telemetry.js
@@ -375,7 +375,7 @@ add_task(async function () {
       );
       info("Waiting for openInsecureButton to be enabled.");
       function callback() {
-        if (!openInsecureButton.inert) {
+        if (!openInsecureButton.classList.contains("disabled")) {
           observer.disconnect();
           content.requestAnimationFrame(() => {
             content.requestAnimationFrame(() => {
@@ -385,7 +385,7 @@ add_task(async function () {
         }
       }
       const observer = new content.MutationObserver(callback);
-      observer.observe(openInsecureButton, { attributeFilter: ["inert"] });
+      observer.observe(openInsecureButton, { attributeFilter: ["class"] });
       callback();
     });
 
diff --git a/dom/security/test/https-only/browser_continue_button_delay.js b/dom/security/test/https-only/browser_continue_button_delay.js
index 6bdee1610e0..a307687bde1 100644
--- a/dom/security/test/https-only/browser_continue_button_delay.js
+++ b/dom/security/test/https-only/browser_continue_button_delay.js
@@ -1,5 +1,21 @@
 "use strict";
 
+// This test ensures the security delay (security.dialog_enable_delay) gets
+// properly applied to the "Continue" button on the HTTPS-Only error page. It
+// consists of the following checks:
+// 1. Check that the button gets enabled at the right time after a new tab is
+//    loaded
+// 2. Check that the button gets disabled and re-enabled with the correct
+//    timings on a focus loss due to a new tab being opened.
+// 3. Check that the button gets enabled with the correct timings when the
+//    HTTPS-Only error page is loaded through the identity pane.
+
+// We specifically want a insecure url here that will fail to upgrade
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const TEST_URL = "http://untrusted.example.com";
+const TEST_PRINCIPAL =
+  Services.scriptSecurityManager.createContentPrincipalFromOrigin(TEST_URL);
+
 function waitForEnabledButton() {
   return new Promise(resolve => {
     const button = content.document.getElementById("openInsecure");
@@ -7,43 +23,28 @@ function waitForEnabledButton() {
       for (const mutation of mutations) {
         if (
           mutation.type === "attributes" &&
-          mutation.attributeName === "inert" &&
-          !mutation.target.inert
+          mutation.attributeName === "class" &&
+          !mutation.target.classList.contains("disabled")
         ) {
           resolve();
         }
       }
     });
-    observer.observe(button, { attributeFilter: ["inert"] });
+    observer.observe(button, { attributeFilter: ["class"] });
     ok(
-      button.inert,
-      "The 'Continue to HTTP Site' button should be inert right after the error page is loaded."
+      button.classList.contains("disabled"),
+      "The 'Continue to HTTP Site' button should be disabled right after the error page is loaded/focused."
     );
   });
 }
 
-add_task(async function () {
-  waitForExplicitFinish();
-
-  await SpecialPowers.pushPrefEnv({
-    set: [["dom.security.https_only_mode", true]],
-  });
-
-  const specifiedDelay = Services.prefs.getIntPref(
-    "security.dialog_enable_delay",
-    1000
-  );
+const specifiedDelay = Services.prefs.getIntPref(
+  "security.dialog_enable_delay",
+  1000
+);
 
-  let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
-  info("Loading insecure page");
+async function waitForEnabledButtonAndCheckTiming() {
   const startTime = Date.now();
-  BrowserTestUtils.startLoadingURIString(
-    gBrowser,
-    // We specifically want a insecure url here that will fail to upgrade
-    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
-    "http://untrusted.example.com:80"
-  );
-  await loaded;
   await SpecialPowers.spawn(gBrowser.selectedBrowser, [], waitForEnabledButton);
   const endTime = Date.now();
 
@@ -54,6 +55,49 @@ add_task(async function () {
     specifiedDelay - 100,
     `The observed delay (${observedDelay}ms) should be roughly the same or greater than the delay specified in "security.dialog_enable_delay" (${specifiedDelay}ms)`
   );
+}
 
-  finish();
+add_task(async function () {
+  await SpecialPowers.pushPrefEnv({
+    set: [["dom.security.https_only_mode", true]],
+  });
+
+  info("Loading insecure page");
+  let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+  BrowserTestUtils.startLoadingURIString(gBrowser, TEST_URL);
+  await loaded;
+  await waitForEnabledButtonAndCheckTiming();
+
+  info("Opening and closing a new tab");
+  let newTab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+  });
+  await BrowserTestUtils.removeTab(newTab);
+  await waitForEnabledButtonAndCheckTiming();
+
+  info("Loading page with exception");
+  await Services.perms.addFromPrincipal(
+    TEST_PRINCIPAL,
+    "https-only-load-insecure",
+    Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION
+  );
+  loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  BrowserTestUtils.startLoadingURIString(gBrowser, TEST_URL);
+  await loaded;
+
+  info("Opening identity pane");
+  document.getElementById("identity-icon-box").click();
+  const identityPopup = document.getElementById("identity-popup");
+  ok(!!identityPopup, "Identity pane should exist");
+  await BrowserTestUtils.waitForPopupEvent(identityPopup, "shown");
+
+  info("Removing exception in identity pane");
+  const menulist = document.getElementById(
+    "identity-popup-security-httpsonlymode-menulist"
+  );
+  ok(!!menulist, "Identity pane should contain HTTPS-Only menulist");
+  loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+  menulist.getItemAtIndex(0).doCommand();
+  await loaded;
+  await waitForEnabledButtonAndCheckTiming();
 });
diff --git a/toolkit/components/httpsonlyerror/tests/browser/head.js b/toolkit/components/httpsonlyerror/tests/browser/head.js
index 7efbfbe8b50..ffe3c79df9e 100644
--- a/toolkit/components/httpsonlyerror/tests/browser/head.js
+++ b/toolkit/components/httpsonlyerror/tests/browser/head.js
@@ -81,7 +81,7 @@ function waitForAndClickOpenInsecureButton(browser) {
     );
     info("Waiting for openInsecureButton to be enabled.");
     function callback() {
-      if (!openInsecureButton.inert) {
+      if (!openInsecureButton.classList.contains("disabled")) {
         info("openInsecureButton was enabled, waiting two frames.");
         observer.disconnect();
         content.requestAnimationFrame(() => {
@@ -93,7 +93,7 @@ function waitForAndClickOpenInsecureButton(browser) {
       }
     }
     const observer = new content.MutationObserver(callback);
-    observer.observe(openInsecureButton, { attributeFilter: ["inert"] });
+    observer.observe(openInsecureButton, { attributeFilter: ["class"] });
     callback();
   });
 }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/dom/security/test/https-first/browser_https_telemetry.js b/dom/security/test/https-first/browser_https_telemetry.js
index d952de1c94e..34242590e9e 100644
--- a/dom/security/test/https-first/browser_https_telemetry.js
+++ b/dom/security/test/https-first/browser_https_telemetry.js
@@ -375,7 +375,7 @@ add_task(async function () {
       );
       info("Waiting for openInsecureButton to be enabled.");
       function callback() {
-        if (!openInsecureButton.inert) {
+        if (!openInsecureButton.classList.contains("disabled")) {
           observer.disconnect();
           content.requestAnimationFrame(() => {
             content.requestAnimationFrame(() => {
@@ -385,7 +385,7 @@ add_task(async function () {
         }
       }
       const observer = new content.MutationObserver(callback);
-      observer.observe(openInsecureButton, { attributeFilter: ["inert"] });
+      observer.observe(openInsecureButton, { attributeFilter: ["class"] });
       callback();
     });
diff --git a/dom/security/test/https-only/browser_continue_button_delay.js b/dom/security/test/https-only/browser_continue_button_delay.js
index 6bdee1610e0..a307687bde1 100644
--- a/dom/security/test/https-only/browser_continue_button_delay.js
+++ b/dom/security/test/https-only/browser_continue_button_delay.js
@@ -1,5 +1,21 @@
 "use strict";
 
+// This test ensures the security delay (security.dialog_enable_delay) gets
+// properly applied to the "Continue" button on the HTTPS-Only error page. It
+// consists of the following checks:
+// 1. Check that the button gets enabled at the right time after a new tab is
+//    loaded
+// 2. Check that the button gets disabled and re-enabled with the correct
+//    timings on a focus loss due to a new tab being opened.
+// 3. Check that the button gets enabled with the correct timings when the
+//    HTTPS-Only error page is loaded through the identity pane.
+
+// We specifically want a insecure url here that will fail to upgrade
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const TEST_URL = "http://untrusted.example.com";
+const TEST_PRINCIPAL =
+  Services.scriptSecurityManager.createContentPrincipalFromOrigin(TEST_URL);
+
 function waitForEnabledButton() {
   return new Promise(resolve => {
     const button = content.document.getElementById("openInsecure");
@@ -7,43 +23,28 @@ function waitForEnabledButton() {
       for (const mutation of mutations) {
         if (
           mutation.type === "attributes" &&
-          mutation.attributeName === "inert" &&
-          !mutation.target.inert
+          mutation.attributeName === "class" &&
+          !mutation.target.classList.contains("disabled")
         ) {
           resolve();
         }
       }
     });
-    observer.observe(button, { attributeFilter: ["inert"] });
+    observer.observe(button, { attributeFilter: ["class"] });
     ok(
-      button.inert,
-      "The 'Continue to HTTP Site' button should be inert right after the error page is loaded."
+      button.classList.contains("disabled"),
+      "The 'Continue to HTTP Site' button should be disabled right after the error page is loaded/focused."
     );
   });
 }
 
-add_task(async function () {
-  waitForExplicitFinish();
-
-  await SpecialPowers.pushPrefEnv({
-    set: [["dom.security.https_only_mode", true]],
-  });
-
-  const specifiedDelay = Services.prefs.getIntPref(
-    "security.dialog_enable_delay",
-    1000
-  );
+const specifiedDelay = Services.prefs.getIntPref(
+  "security.dialog_enable_delay",
+  1000
+);
 
-  let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
-  info("Loading insecure page");
+async function waitForEnabledButtonAndCheckTiming() {
   const startTime = Date.now();
-  BrowserTestUtils.startLoadingURIString(
-    gBrowser,
-    // We specifically want a insecure url here that will fail to upgrade
-    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
-    "http://untrusted.example.com:80"
-  );
-  await loaded;
   await SpecialPowers.spawn(gBrowser.selectedBrowser, [], waitForEnabledButton);
   const endTime = Date.now();
 
@@ -54,6 +55,49 @@ add_task(async function () {
     specifiedDelay - 100,
     `The observed delay (${observedDelay}ms) should be roughly the same or greater than the delay specified in "security.dialog_enable_delay" (${specifiedDelay}ms)`
   );
+}
 
-  finish();
+add_task(async function () {
+  await SpecialPowers.pushPrefEnv({
+    set: [["dom.security.https_only_mode", true]],
+  });
+
+  info("Loading insecure page");
+  let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+  BrowserTestUtils.startLoadingURIString(gBrowser, TEST_URL);
+  await loaded;
+  await waitForEnabledButtonAndCheckTiming();
+
+  info("Opening and closing a new tab");
+  let newTab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+  });
+  await BrowserTestUtils.removeTab(newTab);
+  await waitForEnabledButtonAndCheckTiming();
+
+  info("Loading page with exception");
+  await Services.perms.addFromPrincipal(
+    TEST_PRINCIPAL,
+    "https-only-load-insecure",
+    Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION
+  );
+  loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  BrowserTestUtils.startLoadingURIString(gBrowser, TEST_URL);
+  await loaded;
+
+  info("Opening identity pane");
+  document.getElementById("identity-icon-box").click();
+  const identityPopup = document.getElementById("identity-popup");
+  ok(!!identityPopup, "Identity pane should exist");
+  await BrowserTestUtils.waitForPopupEvent(identityPopup, "shown");
+
+  info("Removing exception in identity pane");
+  const menulist = document.getElementById(
+    "identity-popup-security-httpsonlymode-menulist"
+  );
+  ok(!!menulist, "Identity pane should contain HTTPS-Only menulist");
+  loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+  menulist.getItemAtIndex(0).doCommand();
+  await loaded;
+  await waitForEnabledButtonAndCheckTiming();
 });
diff --git a/toolkit/components/httpsonlyerror/tests/browser/head.js b/toolkit/components/httpsonlyerror/tests/browser/head.js
index 7efbfbe8b50..ffe3c79df9e 100644
--- a/toolkit/components/httpsonlyerror/tests/browser/head.js
+++ b/toolkit/components/httpsonlyerror/tests/browser/head.js
@@ -81,7 +81,7 @@ function waitForAndClickOpenInsecureButton(browser) {
     );
     info("Waiting for openInsecureButton to be enabled.");
     function callback() {
-      if (!openInsecureButton.inert) {
+      if (!openInsecureButton.classList.contains("disabled")) {
         info("openInsecureButton was enabled, waiting two frames.");
         observer.disconnect();
         content.requestAnimationFrame(() => {
@@ -93,7 +93,7 @@ function waitForAndClickOpenInsecureButton(browser) {
       }
     }
     const observer = new content.MutationObserver(callback);
-    observer.observe(openInsecureButton, { attributeFilter: ["inert"] });
+    observer.observe(openInsecureButton, { attributeFilter: ["class"] });
     callback();
   });
 }
Loading diff…