« Firefox webextensions » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
Aucun résumé des modifications |
|||
Ligne 44 : | Ligne 44 : | ||
var mySetting = browser.runtime.getManifest().mySetting; | var mySetting = browser.runtime.getManifest().mySetting; | ||
</filebox> | </filebox> | ||
= [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts Background script] = | = [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts Background script] = | ||
Ligne 63 : | Ligne 59 : | ||
= [https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts Content script] = | = [https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts Content script] = | ||
{{info | Tourne dans le contexte de la page et peut accéder au contenu de la page}} | |||
{{warn | {{boxx|console.log}} ne fonctionne pas ici, utiliser plutôt {{boxx|alert}}}} | |||
Content scripts can read and modify the content of their pages using the standard DOM APIs.<br> | Content scripts can read and modify the content of their pages using the standard DOM APIs.<br> | ||
Content scripts can only access a small subset of the WebExtension APIs, but they can communicate with background scripts using a messaging system, and thereby indirectly access the WebExtension APIs. | Content scripts can only access a small subset of the WebExtension APIs, but they can communicate with background scripts using a messaging system, and thereby indirectly access the WebExtension APIs. | ||
== manifest content_scripts == | |||
<filebox fn='manifest.json'> | <filebox fn='manifest.json'> | ||
// charge le script uniquement si le pattern correspond | |||
"content_scripts": [ | "content_scripts": [ | ||
{ | { | ||
Ligne 71 : | Ligne 72 : | ||
"js": ["content.js"] | "js": ["content.js"] | ||
} | } | ||
] | ] | ||
</filebox> | </filebox> | ||
Ligne 92 : | Ligne 81 : | ||
== [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contentScripts contentScripts API] == | == [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contentScripts contentScripts API] == | ||
== [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/executeScript tabs.executeScript] == | == [https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/executeScript tabs.executeScript API] == | ||
<filebox fn='background.js'> | <filebox fn='background.js'> | ||
browser.tabs.executeScript({ | browser.tabs.executeScript({ |
Version du 6 février 2021 à 22:56
Liens
- https://developer.mozilla.org/en-US/Add-ons/WebExtensions
- fetch
- Pas d'accès au système de fichiers
- Anatomy of an extension
Hierarchie des fichiers
- manifest.json
- main.js
- main.html
- icons
- icon-48.png
- icon-96.png
- content_scripts
manifest.json
manifest.json |
{ // mandatory keys "manifest_version": 2, "name": "Nom de l'extension", "version": "1.0", "description": "description", // taille 48 ou 96 "icons": { "48": "icons/icon-48.png" }, "permissions": [ "tabs" ] } |
Accès au manifest
manifest.json |
{ "mySetting": "value" } |
background.js |
var mySetting = browser.runtime.getManifest().mySetting; |
Background script
Background scripts do not get direct access to web pages.
However, they can load content scripts into web pages and can communicate with these content scripts using a message-passing API.
manifest.json |
"background": { "scripts": ["background.js"] } |
background.js |
Content script
Tourne dans le contexte de la page et peut accéder au contenu de la page |
console.log ne fonctionne pas ici, utiliser plutôt alert |
Content scripts can read and modify the content of their pages using the standard DOM APIs.
Content scripts can only access a small subset of the WebExtension APIs, but they can communicate with background scripts using a messaging system, and thereby indirectly access the WebExtension APIs.
manifest content_scripts
manifest.json |
// charge le script uniquement si le pattern correspond "content_scripts": [ { "matches": ["*://*.domain.net/*"], "js": ["content.js"] } ] |
script.js |
document.body.style.border = "5px solid red"; |
contentScripts API
tabs.executeScript API
background.js |
browser.tabs.executeScript({ code: "alert('xxx');" }); var executing = browser.tabs.executeScript({ file: "modifyPage.js" }); executing.then((result) => { console.log(result); // Array [ 0 ] }, (error) => { console.log(error); }); |
modifyPage.js |
// to avoid the error result is non-structured-clonable data // return a value as a result which is structured clonable. 0; |
Nécessite des permissions d'hôte pour la page dans laquelle on veut injecter du js.
manifest.json |
"permissions": [ "<all_urls>", "*://developer.mozilla.org/*" ] |
insertCSS
background.js |
browser.tabs.insertCSS({ code: "#id { font-size: 64px; }" }); browser.tabs.insertCSS({ file: "style.css" }); |
web_accessible_resources
Security Error: Content at https://www.domain.fr.com may not load or link to moz-extension://<guid>/icons/icon.png.
manifest.json |
"web_accessible_resources": ["icons/icon.png"] |
content.js |
var icon = document.createElement('img'); var iconUrl = browser.extension.getURL("icons/icon.png"); icon.src = iconUrl; |
Communication
Content → Background
content.js |
browser.runtime.sendMessage({"key": value}); |
background.js |
browser.runtime.onMessage.addListener((message) => { console.log("Message received " + message.key); }); |
Background → Content
background.js |
browser.tabs.sendMessage(tabId, {"key": value}); |
content.js |
browser.runtime.onMessage.addListener((message) => { alert("Message received " + message.key); }); |
toolbar button
manifest.json |
"browser_action": { "default_icon": "icons/icon-32.png", "theme_icons": [{ "light": "icons/icon-32-light.png", "dark": "icons/icon-32.png", "size": 32 }], "default_title": "Title" } |
Sans Popup
manifest.json |
"background": { "scripts": ["background.js"] } |
background.js |
function doSomething() { } browser.browserAction.onClicked.addListener(doSomething); |
Popup
manifest.json |
"browser_action": { "default_popup": "popup/actions.html" } |
actions.html |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="actions.css"/> </head> <body> <div>Choix 1</div> <div>Choix 2</div> <script src="actions.js"></script> </body> </html> |
actions.css |
html, body { width: 200px; background-color: black; color: white; } div { border-bottom: solid 1px black; margin: 10px 0; } div:hover { border-color: red; cursor: pointer; } |
actions.js |
document.addEventListener("click", function(e) { if (e.target.tagName != "DIV") { return; } alert(e.target.textContent); }); |
button title and icon
function toggleTitle(title) { if (title == "Old Title") { browser.browserAction.setTitle({ title: "New Title" }); browser.browserAction.setIcon({ path: "new-icon.png" }); } else { browser.browserAction.setTitle({title: "Old Title"}); browser.browserAction.setIcon({ path: "old-icon.png" }); } } browser.browserAction.onClicked.addListener(() => { var gettingTitle = browser.browserAction.getTitle({}); gettingTitle.then(toggleTitle); }); |
Tab
background.js |
// listen to tab URL changes browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) => { if (changeInfo.url) { // changeInfo.url nécessite la permission tabs console.log("URL changed to " + changeInfo.url); console.log("Status " + changeInfo.status); // loading ou complete // regex url if (/https:\/\/www.domain.fr\/path\/.*/.test(changeInfo.url)) { } } }); // listen to tab switching browser.tabs.onActivated.addListener(() => { console.log("tab switching"); }); // active tab var gettingActiveTab = browser.tabs.query({active: true, currentWindow: true}); gettingActiveTab.then((tabs) => { if (tabs[0]) { currentTab = tabs[0]; var tabId = currentTab.id; var tabUrl = currentTab.url; } }); |
content.js |
let currentUrl = window.location.href; |
Download
background.js |
var downloading = browser.downloads.download({ url: myUrl, filename: "image.jpg", // définit le chemin relatif depuis le dossier des téléchargements (créé les dossiers au besoin) saveAs: true, // affiche la fenêtre de choix de l'emplacement incognito: true // téléchargement dans une navigation privé donc pas affiché dans la liste des téléchargements }); downloading.then( (id) => { console.log("ok " + id); }, (error) => { console.log(error); }); |
Request / XMLHttpRequest
background.js |
var request = new XMLHttpRequest(); request.withCredentials = true; // async request.addEventListener("readystatechange", function () { if (this.readyState === 4) { console.log(this.responseText); } }); request.open("GET", "http://www.domain.fr/api/controller"); // sync request.open('GET', 'http://www.domain.fr/api/controller', false); request.setRequestHeader("Content-Type", "application/json"); request.send(null); // request.status → 200 // request.response → { ... } |
Request / fetch
GET
background.js |
fetch(myRequest) .then(response => { // récupération de la réponse if (response.ok) { // test du code de retour // parser les données return response.json(); // json return response.text(); // text return response.blob(); // blob (image) } else { throw new Error('Something went wrong on api server!'); } }) .then(blob => { // données récupérées console.log(blob); // Blob { size: 25248, type: "image/jpeg" } var objectURL = URL.createObjectURL(blob); // blob:moz-extension://guid/guid }).catch(error => { console.error(error); }); |
async / await
async function myFunction() { let data = await (await (fetch(url). then(response => response.blob()))); return data; } |
POST
background.js |
// json var data = JSON.stringify({ key: "value" }); // blob var data = new FormData(); data.append("Content-Type", "multipart/form-data"); data.append("myKey", myBlob); var myHeaders = new Headers(); myHeaders.append('Cache-Control', 'no-cache'); myHeaders.append('Content-Type', 'application/json'); // json const myRequest = new Request('http://www.domain.fr/api/images', { method: 'POST', headers: myHeaders, body: data }); fetch(myRequest).then(response => { if (response.ok) { return response.json(); } else { throw new Error('Something went wrong on api server!'); } }) .then(response => { console.debug(response); }).catch(error => { console.error(error); }); |
415 Unsupported Media Type response
// définir le bon Content-Type var myHeaders = new Headers(); myHeaders.append('Content-Type', 'application/json'); var myRequest = new Request('http://www.domain.fr/api/items', {method: 'POST', headers: myHeaders, body: { "key": "value" }}); |
Cross-Origin Request Blocked
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:59383/api/listapi. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Ajouter les urls dans permissions
manifest.json |
"permissions": [ "<all_urls>" ] |
Fichiers
Window
background.js |
// ouvrir une fenêtre popup contenant un html local var popupURL = browser.extension.getURL("popup.html"); var creating = browser.windows.create({ url: popupURL, type: "popup", state: "maximized", // maximized et fullscreen doivent être utilisés sans popup, height, width, top, left height: 600, width: 800, left: 100, top: 100 }); creating.then((windowInfo) => { console.log(windowInfo); }, (error) => { console.log("Error: " + error); }); |
Test
web-ext
Par défaut, la commande run crée un profil Firefox temporaire. La commande run surveille vos fichiers sources et dit à Firefox de recharger l'extension quand vous éditez et enregistrez un fichier. |
# depuis le dossier contenant les fichiers sources web-ext run # utiliser un profile existant web-ext run --firefox-profile=MyProfile web-ext run --firefox-profile=~/.mozilla/firefox/MyProfile |
Installation
# pacman sudo pacman -S web-ext # npm npm i -g web-ext |
Load Temporary Add-on
about:debugging → Load Temporary Add-on → sélectionner n'importe quel fichier
Ceci installe l'add-on jusqu'au redémarrage de firefox
Debugging
- about:debugging
- Enable add-on debugging
- Load Temporary Add-on ou web-ext run
- Debug (ouvre une fenêtre Developer Tools)
Package your extension
web-ext build # web-ext-artifacts/my_extension-1.0.zip cd /path/to/my-extension zip -r -FS ../my-extension.zip * --exclude '*.git*' |
The generated .zip file won't work on Firefox without signing. |
Sign your extension
Only signed extensions can be installed on Firefox. |
- Developer Hub → Submit a New Add-on
- Once the extension has been validated, an email is sent with a link to the extension versions list
On peut signer un add-on sans le publier sur le site de mozilla. |
Supprimer une extension publiée dans Developer Hub
- Manage My Submissions → My Extension
- View all → Delete Add-on