Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@
"menus.remove",
"menus.update",
"runtime.lastError",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep runtime.lastError as an example, see my comment in background.js.

"tabs.executeScript"
"runtime.onInstalled",
"scripting.executeScript"
],
"name": "menu-demo"
},
Expand Down
26 changes: 10 additions & 16 deletions menu-demo/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion menu-demo/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
},

"menuItemOpenSidebar": {
"message": "Open sidebar",
"message": "Toggle sidebar",
"description": "Title of context menu item that opens a sidebar."
},

Expand Down
181 changes: 90 additions & 91 deletions menu-demo/background.js
Original file line number Diff line number Diff line change
@@ -1,120 +1,121 @@
/*
Called when the item has been created, or when creation failed due to an error.
We'll just log success/failure 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}`);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

menus.create is special, unlike many other extension APIs it does not return a Promise even though it is async.

I think that it would still be useful to log an error to show how error handling works. The "Item created successfully" part can be dropped though.

The console.log call should include a .message though, as in: browser.runtime.lastError.message

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rob--W done, plus some tweaks to the comments

} else {
console.log("Item created successfully");
console.log(`Error: ${browser.runtime.lastError.message}`);
}
}

/*
Called when the item has been removed.
We'll just log success here.
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.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",
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: "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: "separator-2",
type: "separator",
contexts: ["all"]
}, onCreated);

browser.menus.create({
id: "check-uncheck",
type: "checkbox",
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);
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",
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: "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: "separator-2",
type: "separator",
contexts: ["all"]
}, onCreated);

browser.menus.create({
id: "check-uncheck",
type: "checkbox",
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 work 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.
*/
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]
});
}

/*
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) {
Expand All @@ -129,17 +130,15 @@ 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) {
case "log-selection":
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);
Expand All @@ -151,7 +150,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");
Expand Down
20 changes: 13 additions & 7 deletions menu-demo/manifest.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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
}

}
Loading