commit 113d24b27ae7e2932e195beb80c0a0d261325a22
parent dcbcf6b14a80ee09dd7a313bfba1fa43b59007dc
Author: Jeremy Swinarton <jswinarton@mozilla.com>
Date: Thu, 18 Dec 2025 20:31:04 +0000
Bug 2003710: Add 'Update Note' tab context menu entries r=sthompson,fluent-reviewers,tabbrowser-reviewers,bolsson
Differential Revision: https://phabricator.services.mozilla.com/D276921
Diffstat:
5 files changed, 160 insertions(+), 11 deletions(-)
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
@@ -70,7 +70,15 @@
<menuitem id="context_bookmarkTab"
data-lazy-l10n-id="tab-context-bookmark-tab"/>
<menuitem id="context_addNote" hidden="true" data-lazy-l10n-id="tab-context-add-note" />
- <menuitem id="context_editNote" hidden="true" data-lazy-l10n-id="tab-context-edit-note" />
+ <menu id="context_updateNote"
+ hidden="true"
+ data-lazy-l10n-id="tab-context-update-note">
+ <menupopup id="tabUpdateOptionsMenu">
+ <menuitem id="context_editNote" data-lazy-l10n-id="tab-context-edit-note" />
+ <menuitem id="context_deleteNote" data-lazy-l10n-id="tab-context-delete-note" />
+ </menupopup>
+ </menu>
+
<menu id="context_moveTabOptions"
data-lazy-l10n-id="tab-context-move-tabs"
data-l10n-args='{"tabCount": 1}'>
diff --git a/browser/base/content/main-popupset.js b/browser/base/content/main-popupset.js
@@ -88,6 +88,9 @@ document.addEventListener(
case "context_editNote":
gBrowser.tabNoteMenu.openPanel(TabContextMenu.contextTab);
break;
+ case "context_deleteNote":
+ TabContextMenu.deleteTabNotes();
+ break;
case "context_moveToStart":
gBrowser.moveTabsToStart(TabContextMenu.contextTab);
break;
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
@@ -9827,17 +9827,19 @@ var TabContextMenu = {
}
let contextAddNote = document.getElementById("context_addNote");
- let contextEditNote = document.getElementById("context_editNote");
+ let contextUpdateNote = document.getElementById("context_updateNote");
if (gBrowser._tabNotesEnabled) {
- contextAddNote.disabled = !this.TabNotes.isEligible(this.contextTab);
+ contextAddNote.disabled =
+ this.multiselected || !this.TabNotes.isEligible(this.contextTab);
+ contextUpdateNote.disabled = this.multiselected;
this.TabNotes.has(this.contextTab).then(hasNote => {
contextAddNote.hidden = hasNote;
- contextEditNote.hidden = !hasNote;
+ contextUpdateNote.hidden = !hasNote;
});
} else {
contextAddNote.hidden = true;
- contextEditNote.hidden = true;
+ contextUpdateNote.hidden = true;
}
// Split View
@@ -10386,6 +10388,12 @@ var TabContextMenu = {
);
});
},
+
+ deleteTabNotes() {
+ for (let tab of this.contextTabs) {
+ this.TabNotes.delete(tab);
+ }
+ },
};
ChromeUtils.defineESModuleGetters(TabContextMenu, {
diff --git a/browser/components/tabnotes/test/browser/browser_tab_notes_menu.js b/browser/components/tabnotes/test/browser/browser_tab_notes_menu.js
@@ -35,6 +35,58 @@ async function getContextMenu(triggerNode, contextMenuId) {
return contextMenu;
}
+let activateTabContextMenuItem = async (
+ selectedTab,
+ menuItemSelector,
+ submenuItemSelector
+) => {
+ let submenuItem;
+ let submenuItemHiddenPromise;
+
+ const win = selectedTab.ownerGlobal;
+ const tabContextMenu = win.document.getElementById("tabContextMenu");
+ const contextMenuShown = BrowserTestUtils.waitForEvent(
+ tabContextMenu,
+ "popupshown",
+ false,
+ ev => ev.target == tabContextMenu
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ selectedTab,
+ { type: "contextmenu", button: 2 },
+ win
+ );
+ await contextMenuShown;
+
+ if (submenuItemSelector) {
+ submenuItem = tabContextMenu.querySelector(submenuItemSelector);
+
+ const submenuPopupPromise = BrowserTestUtils.waitForEvent(
+ submenuItem.menupopup,
+ "popupshown"
+ );
+ submenuItem.openMenu(true);
+ await submenuPopupPromise;
+
+ submenuItemHiddenPromise = BrowserTestUtils.waitForEvent(
+ submenuItem.menupopup,
+ "popuphidden"
+ );
+ }
+
+ const contextMenuHidden = BrowserTestUtils.waitForEvent(
+ tabContextMenu,
+ "popuphidden",
+ false,
+ ev => ev.target == tabContextMenu
+ );
+ tabContextMenu.activateItem(tabContextMenu.querySelector(menuItemSelector));
+ await contextMenuHidden;
+ if (submenuItemSelector) {
+ await submenuItemHiddenPromise;
+ }
+};
+
/**
* @param {XULMenuElement|XULPopupElement} contextMenu
* @returns {Promise<void>}
@@ -45,11 +97,18 @@ async function closeContextMenu(contextMenu) {
await menuHidden;
}
-async function openTabNoteMenu(tab) {
- let tabContextMenu = await getContextMenu(tab, "tabContextMenu");
+async function openTabNoteMenuByAddNote(tab) {
let tabNotePanel = document.getElementById("tabNotePanel");
let panelShown = BrowserTestUtils.waitForPopupEvent(tabNotePanel, "shown");
- tabContextMenu.activateItem(document.getElementById("context_addNote"));
+ activateTabContextMenuItem(tab, "#context_addNote");
+ await panelShown;
+ return tabNotePanel;
+}
+
+async function openTabNoteMenuByEditNote(tab) {
+ let tabNotePanel = document.getElementById("tabNotePanel");
+ let panelShown = BrowserTestUtils.waitForPopupEvent(tabNotePanel, "shown");
+ activateTabContextMenuItem(tab, "#context_editNote", "#context_updateNote");
await panelShown;
return tabNotePanel;
}
@@ -116,7 +175,7 @@ add_task(async function test_dismissTabNotePanel() {
// Dismiss panel by pressing Esc
let tab = BrowserTestUtils.addTab(gBrowser, "https://www.example.com");
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
- let tabNoteMenu = await openTabNoteMenu(tab);
+ let tabNoteMenu = await openTabNoteMenuByAddNote(tab);
Assert.equal(tabNoteMenu.state, "open", "Tab note menu is open");
EventUtils.synthesizeKey("KEY_Escape");
await BrowserTestUtils.waitForPopupEvent(tabNoteMenu, "hidden");
@@ -127,7 +186,7 @@ add_task(async function test_dismissTabNotePanel() {
);
// Dismiss panel by clicking Cancel
- tabNoteMenu = await openTabNoteMenu(tab);
+ tabNoteMenu = await openTabNoteMenuByAddNote(tab);
Assert.equal(tabNoteMenu.state, "open", "Tab note menu is open");
let menuHidden = BrowserTestUtils.waitForPopupEvent(tabNoteMenu, "hidden");
let cancelButton = document.getElementById("tab-note-editor-button-cancel");
@@ -148,7 +207,7 @@ add_task(async function test_saveTabNote() {
});
let tab = BrowserTestUtils.addTab(gBrowser, "https://www.example.com");
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
- let tabNoteMenu = await openTabNoteMenu(tab);
+ let tabNoteMenu = await openTabNoteMenuByAddNote(tab);
tabNoteMenu.querySelector("textarea").value = "Lorem ipsum dolor";
let menuHidden = BrowserTestUtils.waitForPopupEvent(tabNoteMenu, "hidden");
let tabNoteCreated = BrowserTestUtils.waitForEvent(tab, "TabNote:Created");
@@ -167,3 +226,68 @@ add_task(async function test_saveTabNote() {
await SpecialPowers.popPrefEnv();
});
+
+add_task(async function test_editTabNote() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.notes.enabled", true]],
+ });
+
+ let initialNoteValue = "Lorem ipsum dolor";
+
+ let tab = BrowserTestUtils.addTab(gBrowser, "https://www.example.com");
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ let tabNoteCreated = BrowserTestUtils.waitForEvent(tab, "TabNote:Created");
+ await TabNotes.set(tab, initialNoteValue);
+ await tabNoteCreated;
+
+ let tabNoteMenu = await openTabNoteMenuByEditNote(tab);
+ Assert.equal(
+ tabNoteMenu.querySelector("textarea").value,
+ initialNoteValue,
+ "Tab note panel has initial note value in textarea"
+ );
+
+ let updatedNoteValue = initialNoteValue + " sit amet";
+
+ tabNoteMenu.querySelector("textarea").value = updatedNoteValue;
+ let menuHidden = BrowserTestUtils.waitForPopupEvent(tabNoteMenu, "hidden");
+ let tabNoteEdited = BrowserTestUtils.waitForEvent(tab, "TabNote:Edited");
+ tabNoteMenu.querySelector("#tab-note-editor-button-save").click();
+ await Promise.all([menuHidden, tabNoteEdited]);
+
+ const tabNote = await TabNotes.get(tab);
+ Assert.equal(
+ tabNote.text,
+ updatedNoteValue,
+ "The updated text entered into the textarea was saved as a note"
+ );
+
+ await TabNotes.delete(tab);
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_deleteTabNote() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.notes.enabled", true]],
+ });
+
+ let initialNoteValue = "Lorem ipsum dolor";
+
+ let tab = BrowserTestUtils.addTab(gBrowser, "https://www.example.com");
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ let tabNoteCreated = BrowserTestUtils.waitForEvent(tab, "TabNote:Created");
+ await TabNotes.set(tab, initialNoteValue);
+ await tabNoteCreated;
+
+ let tabNoteRemoved = BrowserTestUtils.waitForEvent(tab, "TabNote:Removed");
+ activateTabContextMenuItem(tab, "#context_deleteNote", "#context_updateNote");
+ await tabNoteRemoved;
+
+ let result = await TabNotes.has(tab);
+
+ Assert.ok(!result, "Tab note was deleted");
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/browser/locales/en-US/browser/tabbrowser.ftl b/browser/locales/en-US/browser/tabbrowser.ftl
@@ -386,9 +386,15 @@ tab-group-context-open-saved-group-in-new-window =
tab-context-add-note =
.label = Add Note
.accesskey = A
+tab-context-update-note =
+ .label = Update Note
+ .accesskey = U
tab-context-edit-note =
.label = Edit Note
.accesskey = E
+tab-context-delete-note =
+ .label = Delete Note
+ .accesskey = D
tab-note-editor-title-create = Add note
tab-note-editor-title-edit = Edit note
tab-note-editor-text-field =