Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionSpoofing issue in the DOM: Copy & Paste and Drag & Drop component
ComponentDOM
Bug ClassLogic Error
Tracker2005081
Fix commiteddf4c14825c (firefox) +64/-1
CISA KEVNot listed
CreditedEdgar Chen
Disclosed2026-01-13

Changed Functions

FunctionChangeNotes
if
browser/base/content/browser.js
modified
add_task
dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
modified

Files Changed

  • browser/base/content/browser.js
  • dom/events/test/clipboard/browser.toml
  • dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
  • toolkit/modules/ClipboardContextMenu.sys.mjs
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 76c996f69ec..b4755a2e5ae 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2250,7 +2250,7 @@ var XULBrowserWindow = {
 
     // Ensure we close any remaining open locationspecific panels
     if (!isSameDocument) {
-      closeOpenPanels("panel[locationspecific='true']");
+      closeOpenPanels(":is(panel, menupopup)[locationspecific='true']");
     }
 
     gPermissionPanel.onLocationChange();
diff --git a/dom/events/test/clipboard/browser.toml b/dom/events/test/clipboard/browser.toml
index 21c5d85a911..cf4ff51406d 100644
--- a/dom/events/test/clipboard/browser.toml
+++ b/dom/events/test/clipboard/browser.toml
@@ -17,6 +17,12 @@ run-if = [
   "os != 'win'", # The popupmenus dismiss when access keys for disabled items are pressed on windows
 ]
 
+["browser_navigator_clipboard_contextmenu_dismiss.js"]
+support-files = [
+  "file_toplevel.html",
+  "file_iframe.html",
+]
+
 ["browser_navigator_clipboard_contextmenu_suppression.js"]
 support-files = [
   "file_toplevel.html",
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
new file mode 100644
index 00000000000..f09c2a9f153
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
@@ -0,0 +1,56 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kContentFileUrl = kBaseUrlForContent + "file_toplevel.html";
+
+async function readText(aBrowser) {
+  return SpecialPowers.spawn(aBrowser, [], () => {
+    content.document.notifyUserGestureActivation();
+    content.eval(`navigator.clipboard.readText();`);
+  });
+}
+
+add_task(async function test_context_menu_dimiss_tab_navigate() {
+  await BrowserTestUtils.withNewTab(kContentFileUrl, async aBrowser => {
+    info(`Randomized text to avoid overlappings with other tests`);
+    await promiseWritingRandomTextToClipboard();
+
+    info(`Wait for paste context menu is shown`);
+    let pasteButtonIsShown = promisePasteButtonIsShown();
+    await readText(aBrowser);
+    await pasteButtonIsShown;
+
+    info("Navigate tab");
+    let pasteButtonIsHidden = promisePasteButtonIsHidden();
+    aBrowser.loadURI(Services.io.newURI("https://example.com/"), {
+      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    });
+
+    info(`Wait for paste context menu is hidden`);
+    await pasteButtonIsHidden;
+  });
+});
+
+add_task(async function test_context_menu_dimiss_tab_reload() {
+  await BrowserTestUtils.withNewTab(kContentFileUrl, async aBrowser => {
+    info(`Randomized text to avoid overlappings with other tests`);
+    await promiseWritingRandomTextToClipboard();
+
+    info(`Wait for paste context menu is shown`);
+    let pasteButtonIsShown = promisePasteButtonIsShown();
+    await readText(aBrowser);
+    await pasteButtonIsShown;
+
+    info("Reload tab");
+    let pasteButtonIsHidden = promisePasteButtonIsHidden();
+    await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
+
+    info(`Wait for paste context menu is hidden`);
+    await pasteButtonIsHidden;
+  });
+});
diff --git a/toolkit/modules/ClipboardContextMenu.sys.mjs b/toolkit/modules/ClipboardContextMenu.sys.mjs
index 2175da37eb5..080b87fecbf 100644
--- a/toolkit/modules/ClipboardContextMenu.sys.mjs
+++ b/toolkit/modules/ClipboardContextMenu.sys.mjs
@@ -164,6 +164,7 @@ export var ClipboardContextMenu = {
     let menupopup = aChromeDoc.createXULElement("menupopup");
     menupopup.id = this.MENU_POPUP_ID;
     menupopup.setAttribute("tabspecific", "true");
+    menupopup.setAttribute("locationspecific", "true");
     menupopup.appendChild(menuitem);
     return menupopup;
   },
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/dom/events/test/clipboard/browser.toml b/dom/events/test/clipboard/browser.toml
index 21c5d85a911..cf4ff51406d 100644
--- a/dom/events/test/clipboard/browser.toml
+++ b/dom/events/test/clipboard/browser.toml
@@ -17,6 +17,12 @@ run-if = [
   "os != 'win'", # The popupmenus dismiss when access keys for disabled items are pressed on windows
 ]
 
+["browser_navigator_clipboard_contextmenu_dismiss.js"]
+support-files = [
+  "file_toplevel.html",
+  "file_iframe.html",
+]
+
 ["browser_navigator_clipboard_contextmenu_suppression.js"]
 support-files = [
   "file_toplevel.html",
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
new file mode 100644
index 00000000000..f09c2a9f153
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_dismiss.js
@@ -0,0 +1,56 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kContentFileUrl = kBaseUrlForContent + "file_toplevel.html";
+
+async function readText(aBrowser) {
+  return SpecialPowers.spawn(aBrowser, [], () => {
+    content.document.notifyUserGestureActivation();
+    content.eval(`navigator.clipboard.readText();`);
+  });
+}
+
+add_task(async function test_context_menu_dimiss_tab_navigate() {
+  await BrowserTestUtils.withNewTab(kContentFileUrl, async aBrowser => {
+    info(`Randomized text to avoid overlappings with other tests`);
+    await promiseWritingRandomTextToClipboard();
+
+    info(`Wait for paste context menu is shown`);
+    let pasteButtonIsShown = promisePasteButtonIsShown();
+    await readText(aBrowser);
+    await pasteButtonIsShown;
+
+    info("Navigate tab");
+    let pasteButtonIsHidden = promisePasteButtonIsHidden();
+    aBrowser.loadURI(Services.io.newURI("https://example.com/"), {
+      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    });
+
+    info(`Wait for paste context menu is hidden`);
+    await pasteButtonIsHidden;
+  });
+});
+
+add_task(async function test_context_menu_dimiss_tab_reload() {
+  await BrowserTestUtils.withNewTab(kContentFileUrl, async aBrowser => {
+    info(`Randomized text to avoid overlappings with other tests`);
+    await promiseWritingRandomTextToClipboard();
+
+    info(`Wait for paste context menu is shown`);
+    let pasteButtonIsShown = promisePasteButtonIsShown();
+    await readText(aBrowser);
+    await pasteButtonIsShown;
+
+    info("Reload tab");
+    let pasteButtonIsHidden = promisePasteButtonIsHidden();
+    await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
+
+    info(`Wait for paste context menu is hidden`);
+    await pasteButtonIsHidden;
+  });
+});
Loading diff…