From 30954488af3e3de2d65f00bfaf450717429db22b Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Thu, 28 May 2026 07:08:29 +1200 Subject: [PATCH 1/3] menu-demo to Manifest v3 --- examples.json | 4 +- menu-demo/README.md | 26 ++--- menu-demo/_locales/en/messages.json | 2 +- menu-demo/background.js | 149 +++++++++++++--------------- menu-demo/manifest.json | 20 ++-- 5 files changed, 96 insertions(+), 105 deletions(-) diff --git a/examples.json b/examples.json index 702fa86b..c535f939 100644 --- a/examples.json +++ b/examples.json @@ -338,8 +338,8 @@ "menus.onClicked", "menus.remove", "menus.update", - "runtime.lastError", - "tabs.executeScript" + "runtime.onInstalled", + "scripting.executeScript" ], "name": "menu-demo" }, diff --git a/menu-demo/README.md b/menu-demo/README.md index ca799202..6c53d6c7 100644 --- a/menu-demo/README.md +++ b/menu-demo/README.md @@ -1,32 +1,26 @@ # menu-demo -A demo of the [menus API](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/). +This extension demonstrates the [menus API](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/). -**This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.** +**This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain doesn't allow that, so this extension doesn't work properly when run on pages in the `addons.mozilla.org` domain.** -**This add-on uses the `menus` namespace to access the functions it needs to create menu items. Note that Chrome, Edge, and Opera all use the `contextMenus` namespace for this, so this extension will not work in these browsers. For compatibility with these browsers, Firefox also offers the `contextMenus` namespace, so to make this extension work with other browsers, use `contextMenus`.** +**This add-on uses the `menus` namespace to access the functions it needs to create menu items. Note that Chrome, Edge, and Opera use the `contextMenus` namespace for this, so this extension doesn't work in these browsers. For compatibility with these browsers, Firefox also offers this API on the `contextMenus` namespace. However, to make this extension work with other browsers, you would need to allow for feature differences when using `contextMenus', such as the lack of support for specifying icons when creating a menu item.** ## What it does -This add-on adds several items to the browser's context menu: - -* one shown when there is a selection in the page, that logs the selected text -to the browser console when clicked. -* one shown in all contexts, that is removed when clicked. -* two "radio" items that are shown in all contexts. -These items are grouped using a separator item on each side. -One radio item adds a blue border to the page, the other adds a green border. -Note that these buttons only work on normal web pages, not special pages -like about:debugging. -* one "checkbox" item, shown in all contexts, whose title is updated when the -item is clicked. +This add-on adds these items to the browser's context menu: + +* one shown when there is a selection in the page, which, when clicked, logs the selected text to the browser console. +* one shown in all contexts, which is removed when clicked. +* two "radio" items that are shown in all contexts. These items are grouped using a separator item on each side. One radio item adds a blue border to the page, the other adds a green border. **Note** These buttons only work on normal web pages, not special pages such as `about:debugging`. +* one "checkbox" item, shown in all contexts, whose title is updated when the item is clicked. * one item that uses the "commands" property to open the add-on's sidebar. It also adds one item to the browser's "Tools" menu. ## What it shows -* How to create various types of menu item: +* How to create various types of menu items: * normal * radio * separator diff --git a/menu-demo/_locales/en/messages.json b/menu-demo/_locales/en/messages.json index 709898f7..d39fb5c7 100644 --- a/menu-demo/_locales/en/messages.json +++ b/menu-demo/_locales/en/messages.json @@ -40,7 +40,7 @@ }, "menuItemOpenSidebar": { - "message": "Open sidebar", + "message": "Toggle sidebar", "description": "Title of context menu item that opens a sidebar." }, diff --git a/menu-demo/background.js b/menu-demo/background.js index 7eb5df36..bd29d4c3 100644 --- a/menu-demo/background.js +++ b/menu-demo/background.js @@ -1,15 +1,3 @@ -/* -Called when the item has been created, or when creation failed due to an error. -We'll just log success/failure here. -*/ -function onCreated() { - if (browser.runtime.lastError) { - console.log(`Error: ${browser.runtime.lastError}`); - } else { - console.log("Item created successfully"); - } -} - /* Called when the item has been removed. We'll just log success here. @@ -29,87 +17,91 @@ function onError(error) { /* Create all the context menu items. */ -browser.menus.create({ - id: "log-selection", - title: browser.i18n.getMessage("menuItemSelectionLogger"), - contexts: ["selection"] -}, onCreated); +browser.runtime.onInstalled.addListener(() => { + browser.menus.create({ + id: "log-selection", + title: browser.i18n.getMessage("menuItemSelectionLogger"), + contexts: ["selection"] + }); -browser.menus.create({ - id: "remove-me", - title: browser.i18n.getMessage("menuItemRemoveMe"), - contexts: ["all"] -}, onCreated); + browser.menus.create({ + id: "remove-me", + title: browser.i18n.getMessage("menuItemRemoveMe"), + contexts: ["all"] + }); -browser.menus.create({ - id: "separator-1", - type: "separator", - contexts: ["all"] -}, onCreated); + browser.menus.create({ + id: "separator-1", + type: "separator", + contexts: ["all"] + }); -browser.menus.create({ - id: "greenify", - type: "radio", - title: browser.i18n.getMessage("menuItemGreenify"), - contexts: ["all"], - checked: true, - icons: { - "16": "icons/paint-green-16.png", - "32": "icons/paint-green-32.png" - } -}, onCreated); + browser.menus.create({ + id: "greenify", + type: "radio", + title: browser.i18n.getMessage("menuItemGreenify"), + contexts: ["all"], + checked: true, + icons: { + "16": "icons/paint-green-16.png", + "32": "icons/paint-green-32.png" + } + }); -browser.menus.create({ - id: "bluify", - type: "radio", - title: browser.i18n.getMessage("menuItemBluify"), - contexts: ["all"], - checked: false, - icons: { - "16": "icons/paint-blue-16.png", - "32": "icons/paint-blue-32.png" - } -}, onCreated); + browser.menus.create({ + id: "bluify", + type: "radio", + title: browser.i18n.getMessage("menuItemBluify"), + contexts: ["all"], + checked: false, + icons: { + "16": "icons/paint-blue-16.png", + "32": "icons/paint-blue-32.png" + } + }); -browser.menus.create({ - id: "separator-2", - type: "separator", - contexts: ["all"] -}, onCreated); + browser.menus.create({ + id: "separator-2", + type: "separator", + contexts: ["all"] + }); -browser.menus.create({ - id: "check-uncheck", - type: "checkbox", - title: browser.i18n.getMessage("menuItemUncheckMe"), - contexts: ["all"], - checked: true, -}, onCreated); + browser.menus.create({ + id: "check-uncheck", + type: "checkbox", + title: browser.i18n.getMessage("menuItemUncheckMe"), + contexts: ["all"], + checked: true, + }); -browser.menus.create({ - id: "open-sidebar", - title: browser.i18n.getMessage("menuItemOpenSidebar"), - contexts: ["all"], - command: "_execute_sidebar_action" -}, onCreated); + browser.menus.create({ + id: "open-sidebar", + title: browser.i18n.getMessage("menuItemOpenSidebar"), + contexts: ["all"], + command: "_execute_sidebar_action" + }); -browser.menus.create({ - id: "tools-menu", - title: browser.i18n.getMessage("menuItemToolsMenu"), - contexts: ["tools_menu"], -}, onCreated); + browser.menus.create({ + id: "tools-menu", + title: browser.i18n.getMessage("menuItemToolsMenu"), + contexts: ["tools_menu"], + }); +}); /* Set a colored border on the document in the given tab. -Note that this only work on normal web pages, not special pages +Note that this only works on normal web pages, not special pages like about:debugging. */ -let blue = 'document.body.style.border = "5px solid blue"'; -let green = 'document.body.style.border = "5px solid green"'; +const blue = "5px solid blue"; +const green = "5px solid green"; function borderify(tabId, color) { - browser.tabs.executeScript(tabId, { - code: color + browser.scripting.executeScript({ + target: { tabId }, + func: (border) => { document.body.style.border = border; }, + args: [color] }); } @@ -138,8 +130,7 @@ browser.menus.onClicked.addListener((info, tab) => { console.log(info.selectionText); break; case "remove-me": - let removing = browser.menus.remove(info.menuItemId); - removing.then(onRemoved, onError); + browser.menus.remove(info.menuItemId).then(onRemoved, onError); break; case "bluify": borderify(tab.id, blue); diff --git a/menu-demo/manifest.json b/menu-demo/manifest.json index 59c12ea6..9c36d4ff 100644 --- a/menu-demo/manifest.json +++ b/menu-demo/manifest.json @@ -1,25 +1,30 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "__MSG_extensionName__", "description": "__MSG_extensionDescription__", "version": "1.0", "default_locale": "en", "browser_specific_settings": { "gecko": { - "strict_min_version": "56.0a1" + "id": "menu-demo@mozilla.org", + "strict_min_version": "109.0" + }, + "data_collection_permissions": { + "required": ["none"] } }, "background": { "scripts": ["background.js"] }, - + "permissions": [ "menus", - "activeTab" + "activeTab", + "scripting" ], - + "icons": { "16": "icons/page-16.png", "32": "icons/page-32.png", @@ -28,8 +33,9 @@ "sidebar_action": { "default_icon": "icons/page-32.png", - "default_title" : "My sidebar", - "default_panel": "sidebar/sidebar.html" + "default_title": "My sidebar", + "default_panel": "sidebar/sidebar.html", + "open_at_install": false } } From 38839ee9ef06f80582446ec1c4bcca8c011c9a6f Mon Sep 17 00:00:00 2001 From: rebloor Date: Fri, 29 May 2026 04:35:08 +1200 Subject: [PATCH 2/3] Apply suggestion from @Rob--W Co-authored-by: Rob Wu --- menu-demo/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu-demo/background.js b/menu-demo/background.js index bd29d4c3..68636508 100644 --- a/menu-demo/background.js +++ b/menu-demo/background.js @@ -142,7 +142,7 @@ browser.menus.onClicked.addListener((info, tab) => { updateCheckUncheck(info.checked); break; case "open-sidebar": - console.log("Opening my sidebar"); + console.warn("_execute_sidebar_action not supported"); break; case "tools-menu": console.log("Clicked the tools menu item"); From e7e3af16c858914a976b20547c3965702a74ecf5 Mon Sep 17 00:00:00 2001 From: Richard Bloor Date: Fri, 29 May 2026 05:14:58 +1200 Subject: [PATCH 3/3] Restores capture of errors on the menu item creation --- examples.json | 1 + menu-demo/background.js | 48 ++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/examples.json b/examples.json index c535f939..9321fbde 100644 --- a/examples.json +++ b/examples.json @@ -338,6 +338,7 @@ "menus.onClicked", "menus.remove", "menus.update", + "runtime.lastError", "runtime.onInstalled", "scripting.executeScript" ], diff --git a/menu-demo/background.js b/menu-demo/background.js index 68636508..7b9edc88 100644 --- a/menu-demo/background.js +++ b/menu-demo/background.js @@ -1,40 +1,50 @@ /* -Called when the item has been removed. -We'll just log success here. +Called when a menu item is created, or when creation fails due to an error. +We log the error here. +*/ +function onCreated() { + if (browser.runtime.lastError) { + console.log(`Error: ${browser.runtime.lastError.message}`); + } +} + +/* +Called when the menu item is removed. +We log success here. */ function onRemoved() { console.log("Item removed successfully"); } /* -Called when there was an error. -We'll just log the error here. +Called when there is an error in removing a menu item. +We log the error here. */ function onError(error) { console.log(`Error: ${error}`); } /* -Create all the context menu items. +Creates all the context menu items. */ browser.runtime.onInstalled.addListener(() => { browser.menus.create({ id: "log-selection", title: browser.i18n.getMessage("menuItemSelectionLogger"), contexts: ["selection"] - }); + }, onCreated); browser.menus.create({ id: "remove-me", title: browser.i18n.getMessage("menuItemRemoveMe"), contexts: ["all"] - }); + }, onCreated); browser.menus.create({ id: "separator-1", type: "separator", contexts: ["all"] - }); + }, onCreated); browser.menus.create({ id: "greenify", @@ -46,7 +56,7 @@ browser.runtime.onInstalled.addListener(() => { "16": "icons/paint-green-16.png", "32": "icons/paint-green-32.png" } - }); + }, onCreated); browser.menus.create({ id: "bluify", @@ -58,13 +68,13 @@ browser.runtime.onInstalled.addListener(() => { "16": "icons/paint-blue-16.png", "32": "icons/paint-blue-32.png" } - }); + }, onCreated); browser.menus.create({ id: "separator-2", type: "separator", contexts: ["all"] - }); + }, onCreated); browser.menus.create({ id: "check-uncheck", @@ -72,27 +82,26 @@ browser.runtime.onInstalled.addListener(() => { title: browser.i18n.getMessage("menuItemUncheckMe"), contexts: ["all"], checked: true, - }); + }, onCreated); browser.menus.create({ id: "open-sidebar", title: browser.i18n.getMessage("menuItemOpenSidebar"), contexts: ["all"], command: "_execute_sidebar_action" - }); + }, onCreated); browser.menus.create({ id: "tools-menu", title: browser.i18n.getMessage("menuItemToolsMenu"), contexts: ["tools_menu"], - }); + }, onCreated); }); /* -Set a colored border on the document in the given tab. +Sets a colored border on the document in the tab returned by the onClicked listener. -Note that this only works on normal web pages, not special pages -like about:debugging. +Note that this only works on normal web pages, not special pages, such as about:debugging. */ const blue = "5px solid blue"; const green = "5px solid green"; @@ -106,7 +115,7 @@ function borderify(tabId, color) { } /* -Update the menu item's title according to current "checked" value. +Updates the menu item's title according to "checked" value. */ function updateCheckUncheck(checkedState) { if (checkedState) { @@ -121,8 +130,7 @@ function updateCheckUncheck(checkedState) { } /* -The click event listener, where we perform the appropriate action given the -ID of the menu item that was clicked. +The click event listener, where the extension performs the appropriate action given the ID of the menu item clicked. */ browser.menus.onClicked.addListener((info, tab) => { switch (info.menuItemId) {