Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions beastify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Note that:

* if the user reloads the tab, or switches tabs, while the popup is open, then the popup won't be able to beastify the page any more (because the content script was injected into the original tab).

* by default [`tabs.executeScript()`](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/executeScript) injects the script only when the web page and its resources have finished loading. This means that clicks in the popup will have no effect until the page has finished loading.
* by default [`scripting.executeScript()`](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/scripting/executeScript) injects the script only when the web page and its resources have finished loading. This means that clicks in the popup will have no effect until the page has finished loading.

* it's not possible to inject content scripts into certain pages, including privileged browser pages like "about:debugging" and the [addons.mozilla.org](https://addons.mozilla.org/) website. If the user clicks the beastify icon when such a page is loaded into the active tab, the popup displays an error message.

Expand All @@ -34,7 +34,7 @@ Note that:
* write a browser action with a popup
* how to have different browser_action images based upon the theme
* give the popup style and behavior using CSS and JS
* inject a content script programmatically using `tabs.executeScript()`
* inject a content script programmatically using `scripting.executeScript()`
* send a message from the main extension to a content script
* use web accessible resources to enable web pages to load packaged content
* reload web pages
21 changes: 17 additions & 4 deletions beastify/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{

"description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#beastify",
"manifest_version": 2,
"manifest_version": 3,
"name": "Beastify",
"version": "1.0",
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
Expand All @@ -10,10 +10,20 @@
},

"permissions": [
"activeTab"
"activeTab",
"scripting"
],

"browser_action": {
"browser_specific_settings": {
"gecko": {
"id": "[email protected]",
Copy link
Contributor

@dotproto dotproto Nov 17, 2025

Choose a reason for hiding this comment

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

See this comment for details.

Suggested change
"id": "beastify@example.com",
"id": "beastify@mozilla.org",

"data_collection_permissions": {
"required": ["none"]
}
}
},

"action": {
"default_icon": "icons/beasts-32.png",
"theme_icons": [{
"light": "icons/beasts-32-light.png",
Expand All @@ -25,7 +35,10 @@
},

"web_accessible_resources": [
"beasts/*.jpg"
{
"resources": [ "beasts/*.jpg" ],
"matches": [ "*://*/*" ]
}
]

}
54 changes: 37 additions & 17 deletions beastify/popup/choose_beast.js
Copy link
Contributor

Choose a reason for hiding this comment

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

Revise to use async/await instead of Promise chains.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const hidePage = `body > :not(.beastify-image) {
*/
function listenForClicks() {
document.addEventListener("click", (e) => {

/**
* Given the name of a beast, get the URL to the corresponding image.
*/
Expand All @@ -33,25 +32,37 @@ function listenForClicks() {
* send a "beastify" message to the content script in the active tab.
*/
function beastify(tabs) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
function beastify(tabs) {
async function beastify(tabs) {

browser.tabs.insertCSS({code: hidePage}).then(() => {
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabs[0].id, {
command: "beastify",
beastURL: url
const tabId = tabs[0].id;
browser.scripting
.insertCSS({
target: { tabId },
css: hidePage,
})
.then(() => {
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabId, {
command: "beastify",
beastURL: url,
});
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Revise the function to use async/await.

Warning

This function now takes a single Tab rather than an array of Tabs. Call sites must be updated.

Suggested change
function beastify(tabs) {
browser.tabs.insertCSS({code: hidePage}).then(() => {
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabs[0].id, {
command: "beastify",
beastURL: url
const tabId = tabs[0].id;
browser.scripting
.insertCSS({
target: { tabId },
css: hidePage,
})
.then(() => {
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabId, {
command: "beastify",
beastURL: url,
});
});
});
}
async function beastify(tab) {
await browser.scripting.insertCSS({
target: { tabId: tab.id },
css: hidePage,
});
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabId, {
command: "beastify",
beastURL: url,
});
}


/**
* Remove the page-hiding CSS from the active tab,
* send a "reset" message to the content script in the active tab.
*/
function reset(tabs) {
browser.tabs.removeCSS({code: hidePage}).then(() => {
browser.tabs.sendMessage(tabs[0].id, {
command: "reset",
const tabId = tabs[0].id;
browser.scripting
.removeCSS({
target: { tabId },
css: hidePage,
})
.then(() => {
browser.tabs.sendMessage(tabId, {
command: "reset",
});
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Revise the function to use async/await.

Warning

This function has also been revised to expect a single tab rather than an array of tabs. As such, call sites will also need to be updated.

Suggested change
function reset(tabs) {
browser.tabs.removeCSS({code: hidePage}).then(() => {
browser.tabs.sendMessage(tabs[0].id, {
command: "reset",
const tabId = tabs[0].id;
browser.scripting
.removeCSS({
target: { tabId },
css: hidePage,
})
.then(() => {
browser.tabs.sendMessage(tabId, {
command: "reset",
});
});
});
}
async function reset(tab) {
await browser.scripting.removeCSS({
target: { tabId: tab.id },
css: hidePage,
});
browser.tabs.sendMessage(tabId, { command: "reset" });
}


/**
Expand All @@ -68,13 +79,15 @@ function listenForClicks() {
if (e.target.tagName !== "BUTTON" || !e.target.closest("#popup-content")) {
// Ignore when click is not on a button within <div id="popup-content">.
return;
}
}
if (e.target.type === "reset") {
browser.tabs.query({active: true, currentWindow: true})
browser.tabs
.query({ active: true, currentWindow: true })
.then(reset)
.catch(reportError);
} else {
browser.tabs.query({active: true, currentWindow: true})
browser.tabs
.query({ active: true, currentWindow: true })
.then(beastify)
.catch(reportError);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Revise the if block to use async/await conventions.

Since the query for the current tab is common to both blocks, we can move it above the if statements. Similarly, since the reportError error handler is common across both branches of the if statement, we can move it into a catch that wraps the entire if/else block.

Suggested change
if (e.target.type === "reset") {
browser.tabs.query({active: true, currentWindow: true})
browser.tabs
.query({ active: true, currentWindow: true })
.then(reset)
.catch(reportError);
} else {
browser.tabs.query({active: true, currentWindow: true})
browser.tabs
.query({ active: true, currentWindow: true })
.then(beastify)
.catch(reportError);
}
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
try {
if (e.target.type === "reset") {
await reset(tab);
} else {
await beastify(tab);
}
} catch (error) {
reportError(error);
}

Expand All @@ -96,6 +109,13 @@ function reportExecuteScriptError(error) {
* and add a click handler.
* If we couldn't inject the script, handle the error.
*/
browser.tabs.executeScript({file: "/content_scripts/beastify.js"})
.then(listenForClicks)
.catch(reportExecuteScriptError);
browser.tabs
.query({ active: true, currentWindow: true })
.then(([tab]) =>
browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["/content_scripts/beastify.js"],
})
)
.then(listenForClicks)
.catch(reportExecuteScriptError);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
browser.tabs
.query({ active: true, currentWindow: true })
.then(([tab]) =>
browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["/content_scripts/beastify.js"],
})
)
.then(listenForClicks)
.catch(reportExecuteScriptError);
(function runOnPopupOpened() {
try {
const [tab] = browser.tabs.query({ active: true, currentWindow: true });
await browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["/content_scripts/beastify.js"],
});
listenForClicks();
} catch(e) {
reportExecuteScriptError(e);
}
})();

11 changes: 10 additions & 1 deletion borderify/manifest.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
{

"description": "Adds a solid red border to all webpages matching mozilla.org. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#borderify",
"manifest_version": 2,
"manifest_version": 3,
"name": "Borderify",
"version": "1.0",
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/borderify",
"icons": {
"48": "icons/border-48.png"
},

"browser_specific_settings": {
"gecko": {
"id": "[email protected]",
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like the prevailing pattern in for the examples in this repo is <example-name>@mozilla.org where example-name is a kebab case identifier that resembles the example's parent directory. This pattern has the benefit of being reserved on AMO, so developers cannot submit add-ons with these IDs while testing.

Suggested change
"id": "borderify@example.com",
"id": "borderify@mozilla.org",

"data_collection_permissions": {
"required": ["none"]
}
}
},

"content_scripts": [
{
"matches": ["*://*.mozilla.org/*"],
Expand Down
Loading