mirror of
https://github.com/mdn/webextensions-examples.git
synced 2026-04-17 14:59:12 +02:00
MDN get started tutorial examples to manifest V3 (#608)
* MDN get started tutorial examples to manifest V3 * Migrate to Scripting API * Updated to readmes and code comments * Apply suggestions to readmes from review Co-authored-by: Simeon Vincent <svincent@gmail.com> * Convert choose_beast.js to async * Apply suggestions from review Co-authored-by: Simeon Vincent <svincent@gmail.com> * Updated illustrated API list for beastify * Apply suggestions from review Co-authored-by: Rob Wu <rob@robwu.nl> --------- Co-authored-by: Simeon Vincent <svincent@gmail.com> Co-authored-by: Rob Wu <rob@robwu.nl>
This commit is contained in:
@@ -1,40 +1,41 @@
|
|||||||
# beastify
|
# beastify
|
||||||
|
|
||||||
|
This extension used a tootlbar button to enable the section of beast that replaces the content of the active web page.
|
||||||
|
|
||||||
## What it does ##
|
## What it does ##
|
||||||
|
|
||||||
The extension includes:
|
The extension includes:
|
||||||
|
|
||||||
* a browser action with a popup including HTML, CSS, and JS
|
* An action with a popup that includes HTML, CSS, and JavaScript.
|
||||||
* a content script
|
* A content script.
|
||||||
* three images, each of a different beast, packaged as web accessible resources
|
* Three images, each of a beast, packaged as web accessible resources.
|
||||||
|
|
||||||
When the user clicks the browser action button, the popup is shown, enabling
|
When the user clicks the action (toolbar button), the extension's popup opens, enabling the user to choose one of three beasts.
|
||||||
the user to choose one of three beasts.
|
|
||||||
|
|
||||||
When it is shown, the popup injects a content script into the current page.
|
When opened, the popup injects a content script into the active page.
|
||||||
|
|
||||||
When the user chooses a beast, the extension sends the content script a message containing
|
When the user chooses a beast, the extension sends the content script a message containing the name of the chosen beast.
|
||||||
the name of the chosen beast.
|
|
||||||
|
|
||||||
When the content script receives this message, it replaces the current page
|
When the content script receives this message, it replaces the active page content with an image of the chosen beast.
|
||||||
content with an image of the chosen beast.
|
|
||||||
|
|
||||||
When the user clicks the reset button, the page reloads, and reverts to its original form.
|
When the user clicks the reset button, the page reloads and reverts to its original form.
|
||||||
|
|
||||||
Note that:
|
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).
|
* If the user reloads the tab, or switches tabs, while the popup is open, then the popup can't beastify the page (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 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.
|
* Browsers don't allow extensions to inject content scripts into specific pages. In Firefox, this includes privileged browser pages, such as "about:debugging", and the [addons.mozilla.org](https://addons.mozilla.org/) website. In Chrome, this includes internal pages, such as `chrome://extensions`, and the [chromewebstore.google.com](https://chromewebstore.google.com/) website. If the user clicks the beastify icon on one of these pages, the popup displays an error message.
|
||||||
|
|
||||||
## What it shows ##
|
## What it shows ##
|
||||||
|
|
||||||
* write a browser action with a popup
|
In this example, you see how to:
|
||||||
* how to have different browser_action images based upon the theme
|
|
||||||
* give the popup style and behavior using CSS and JS
|
* Write an action (toolbar button) with a popup.
|
||||||
* inject a content script programmatically using `tabs.executeScript()`
|
* Display action (toolbar button) icons based on the browser theme.
|
||||||
* send a message from the main extension to a content script
|
* Give a popup style and behavior using CSS and JavaScript.
|
||||||
* use web accessible resources to enable web pages to load packaged content
|
* Inject a content script programmatically using `scripting.executeScript()`.
|
||||||
* reload web pages
|
* 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.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
(function() {
|
(function() {
|
||||||
/**
|
/**
|
||||||
* Check and set a global guard variable.
|
* Check and set a global guard variable to
|
||||||
* If this content script is injected into the same page again,
|
* ensure that if this content script is injected into a page again,
|
||||||
* it will do nothing next time.
|
* it returns (and does nothing).
|
||||||
*/
|
*/
|
||||||
if (window.hasRun) {
|
if (window.hasRun) {
|
||||||
return;
|
return;
|
||||||
@@ -10,21 +10,24 @@
|
|||||||
window.hasRun = true;
|
window.hasRun = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a URL to a beast image, remove all existing beasts, then
|
* Given a URL for a beast image, remove all beasts,
|
||||||
* create and style an IMG node pointing to
|
* then create and style an IMG node pointing to the image and
|
||||||
* that image, then insert the node into the document.
|
* insert the node into the document.
|
||||||
*/
|
*/
|
||||||
function insertBeast(beastURL) {
|
function insertBeast(beastURL) {
|
||||||
removeExistingBeasts();
|
removeExistingBeasts();
|
||||||
let beastImage = document.createElement("img");
|
let beastImage = document.createElement("img");
|
||||||
beastImage.setAttribute("src", beastURL);
|
beastImage.setAttribute("src", beastURL);
|
||||||
beastImage.style.height = "100vh";
|
beastImage.style.objectFit = "contain";
|
||||||
|
beastImage.style.position = "fixed";
|
||||||
|
beastImage.style.height = "100%";
|
||||||
|
beastImage.style.width = "100%";
|
||||||
beastImage.className = "beastify-image";
|
beastImage.className = "beastify-image";
|
||||||
document.body.appendChild(beastImage);
|
document.body.appendChild(beastImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove every beast from the page.
|
* Remove all beasts from the page.
|
||||||
*/
|
*/
|
||||||
function removeExistingBeasts() {
|
function removeExistingBeasts() {
|
||||||
let existingBeasts = document.querySelectorAll(".beastify-image");
|
let existingBeasts = document.querySelectorAll(".beastify-image");
|
||||||
@@ -35,7 +38,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen for messages from the background script.
|
* Listen for messages from the background script.
|
||||||
* Call "beastify()" or "reset()".
|
* Depending on the message, call "beastify()" or "reset()".
|
||||||
*/
|
*/
|
||||||
browser.runtime.onMessage.addListener((message) => {
|
browser.runtime.onMessage.addListener((message) => {
|
||||||
if (message.command === "beastify") {
|
if (message.command === "beastify") {
|
||||||
|
|||||||
@@ -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",
|
"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",
|
"name": "Beastify",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
|
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
|
||||||
@@ -10,10 +10,20 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"activeTab"
|
"activeTab",
|
||||||
|
"scripting"
|
||||||
],
|
],
|
||||||
|
|
||||||
"browser_action": {
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "beastify@mozilla.org",
|
||||||
|
"data_collection_permissions": {
|
||||||
|
"required": ["none"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"action": {
|
||||||
"default_icon": "icons/beasts-32.png",
|
"default_icon": "icons/beasts-32.png",
|
||||||
"theme_icons": [{
|
"theme_icons": [{
|
||||||
"light": "icons/beasts-32-light.png",
|
"light": "icons/beasts-32-light.png",
|
||||||
@@ -25,7 +35,10 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
"beasts/*.jpg"
|
{
|
||||||
|
"resources": [ "beasts/*.jpg" ],
|
||||||
|
"matches": [ "*://*/*" ]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* CSS to hide everything on the page,
|
* CSS to hide everything on the page,
|
||||||
* except for elements that have the "beastify-image" class.
|
* except for elements that have the ".beastify-image" class.
|
||||||
*/
|
*/
|
||||||
const hidePage = `body > :not(.beastify-image) {
|
const hidePage = `body > :not(.beastify-image) {
|
||||||
display: none;
|
display: none !important;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,10 +11,9 @@ const hidePage = `body > :not(.beastify-image) {
|
|||||||
* the content script in the page.
|
* the content script in the page.
|
||||||
*/
|
*/
|
||||||
function listenForClicks() {
|
function listenForClicks() {
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", async (e) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the name of a beast, get the URL to the corresponding image.
|
* Given the name of a beast, get the URL for the corresponding image.
|
||||||
*/
|
*/
|
||||||
function beastNameToURL(beastName) {
|
function beastNameToURL(beastName) {
|
||||||
switch (beastName) {
|
switch (beastName) {
|
||||||
@@ -29,33 +28,35 @@ function listenForClicks() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the page-hiding CSS into the active tab,
|
* Insert the page-hiding CSS into the active tab,
|
||||||
* then get the beast URL and
|
* get the beast URL, and
|
||||||
* send a "beastify" message to the content script in the active tab.
|
* send a "beastify" message to the content script in the active tab.
|
||||||
*/
|
*/
|
||||||
function beastify(tabs) {
|
async function beastify(tab) {
|
||||||
browser.tabs.insertCSS({code: hidePage}).then(() => {
|
await browser.scripting.insertCSS({
|
||||||
const url = beastNameToURL(e.target.textContent);
|
target: { tabId: tab.id },
|
||||||
browser.tabs.sendMessage(tabs[0].id, {
|
css: hidePage,
|
||||||
command: "beastify",
|
});
|
||||||
beastURL: url
|
const url = beastNameToURL(e.target.textContent);
|
||||||
});
|
await browser.tabs.sendMessage(tab.id, {
|
||||||
|
command: "beastify",
|
||||||
|
beastURL: url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the page-hiding CSS from the active tab,
|
* Remove the page-hiding CSS from the active tab and
|
||||||
* send a "reset" message to the content script in the active tab.
|
* send a "reset" message to the content script in the active tab.
|
||||||
*/
|
*/
|
||||||
function reset(tabs) {
|
async function reset(tab) {
|
||||||
browser.tabs.removeCSS({code: hidePage}).then(() => {
|
await browser.scripting.removeCSS({
|
||||||
browser.tabs.sendMessage(tabs[0].id, {
|
target: { tabId: tab.id },
|
||||||
command: "reset",
|
css: hidePage,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
await browser.tabs.sendMessage(tab.id, { command: "reset" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just log the error to the console.
|
* Log the error to the console.
|
||||||
*/
|
*/
|
||||||
function reportError(error) {
|
function reportError(error) {
|
||||||
console.error(`Could not beastify: ${error}`);
|
console.error(`Could not beastify: ${error}`);
|
||||||
@@ -68,15 +69,18 @@ function listenForClicks() {
|
|||||||
if (e.target.tagName !== "BUTTON" || !e.target.closest("#popup-content")) {
|
if (e.target.tagName !== "BUTTON" || !e.target.closest("#popup-content")) {
|
||||||
// Ignore when click is not on a button within <div id="popup-content">.
|
// Ignore when click is not on a button within <div id="popup-content">.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (e.target.type === "reset") {
|
|
||||||
browser.tabs.query({active: true, currentWindow: true})
|
try {
|
||||||
.then(reset)
|
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
.catch(reportError);
|
|
||||||
} else {
|
if (e.target.type === "reset") {
|
||||||
browser.tabs.query({active: true, currentWindow: true})
|
await reset(tab);
|
||||||
.then(beastify)
|
} else {
|
||||||
.catch(reportError);
|
await beastify(tab);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reportError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -92,10 +96,20 @@ function reportExecuteScriptError(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the popup loads, inject a content script into the active tab,
|
* When the popup loads, inject a content script into the active tab
|
||||||
* and add a click handler.
|
* and add a click handler.
|
||||||
* If we couldn't inject the script, handle the error.
|
* If the extension couldn't inject the script, handle the error.
|
||||||
*/
|
*/
|
||||||
browser.tabs.executeScript({file: "/content_scripts/beastify.js"})
|
(async function runOnPopupOpened() {
|
||||||
.then(listenForClicks)
|
try {
|
||||||
.catch(reportExecuteScriptError);
|
const [tab] = await 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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
# borderify
|
# borderify
|
||||||
|
|
||||||
**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 mozilla.org web pages.
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|
||||||
This extension just includes:
|
This extension includes a content script, "borderify.js", that is injected into any pages
|
||||||
|
under "mozilla.org/" or any of its subdomains.
|
||||||
|
|
||||||
* a content script, "borderify.js", that is injected into any pages
|
**The `addons.mozilla.org` domain doesn't allow scripts to be injected into its pages. Therefore, this extension doesn't work on pages in the `addons.mozilla.org` domain.**
|
||||||
under "mozilla.org/" or any of its subdomains
|
|
||||||
|
|
||||||
The content script draws a border around the document.body.
|
The content script draws a border around the document.body.
|
||||||
|
|
||||||
## What it shows
|
## What it shows
|
||||||
|
|
||||||
* how to inject content scripts declaratively using manifest.json
|
From this example, you see how to inject content scripts declaratively using manifest.json.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
Just draw a border round the document.body.
|
Draw a border round the document.body.
|
||||||
*/
|
*/
|
||||||
document.body.style.border = "5px solid red";
|
document.body.style.border = "5px solid red";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
"description": "Adds a solid red border to all webpages matching mozilla.org. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#borderify",
|
"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",
|
"name": "Borderify",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/borderify",
|
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/borderify",
|
||||||
@@ -9,6 +9,15 @@
|
|||||||
"48": "icons/border-48.png"
|
"48": "icons/border-48.png"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "borderify@mozilla.org",
|
||||||
|
"data_collection_permissions": {
|
||||||
|
"required": ["none"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["*://*.mozilla.org/*"],
|
"matches": ["*://*.mozilla.org/*"],
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
{
|
{
|
||||||
"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.",
|
"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.",
|
||||||
"javascript_apis": [
|
"javascript_apis": [
|
||||||
"extension.getURL",
|
"runtime.getURL",
|
||||||
"runtime.onMessage",
|
"runtime.onMessage",
|
||||||
|
"scripting.insertCSS",
|
||||||
|
"scripting.removeCSS",
|
||||||
"tabs.executeScript",
|
"tabs.executeScript",
|
||||||
"tabs.insertCSS",
|
|
||||||
"tabs.query",
|
"tabs.query",
|
||||||
"tabs.removeCSS",
|
|
||||||
"tabs.sendMessage",
|
"tabs.sendMessage",
|
||||||
"tabs.Tab"
|
"tabs.Tab"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user