Liens
Hierarchie des fichiers
- manifest.json
- main.js
- main.html
- icons
- content_scripts
manifest.json
manifest.json
|
{
"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" ]
}
|
permissions
manifest.json
|
{
"mySetting": "value"
}
|
background.js
|
var mySetting = browser.runtime.getManifest().mySetting;
|
|
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 |
manifest.json
|
"background": {
"scripts": ["background.js"]
}
|
Injecter du JS / CSS dans la page
content_scripts manifest.json key
manifest.json
|
// charge le script si le pattern correspond
"content_scripts": [
{
"matches": ["*://*.mozilla.org/*"],
"js": ["script.js"]
}
]
|
script.js
|
document.body.style.border = "5px solid red";
|
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/*" ]
|
background.js
|
browser.tabs.insertCSS({
code: "#id { font-size: 64px; }"
});
browser.tabs.insertCSS({
file: "style.css"
});
|
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;
|
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);
});
|
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"
}
|
manifest.json
|
"background": {
"scripts": ["background.js"]
}
|
background.js
|
function doSomething() { }
browser.browserAction.onClicked.addListener(doSomething);
|
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;
|
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 → { ... }
|
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>" ]
|
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
|
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
# forcer une version de firefox et un profile
web-ext run --firefox="C:\Program Files\Firefox Developer Edition\firefox.exe" --firefox-profile=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
- 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*'
|
Sign an 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. |
- Manage My Submissions
- Mon Extension → More → Manage Status and Version → Delete Add-on