visit
I've written multiple posts about my conference submission workflow. To sum up:
AS A: Lazy developer I WANT TO: Automatically add CFP data on Trello while browsing a web page on Papercall or Sessionize SO AS: To spend my time doing more fun stuff than copy-paste-- My single user story
{
"manifest_version": 2,
"name": "Borderify",
"version": "1.0",
"content_scripts": [
{
"js": ["borderify.js"]
}
]
}
document.body.style.border = '5px solid red';
Then, point to your manifest file. Firefox loads the extension: it's now active.
In the above example, the JavaScript from the tutorial adds a red border around every web page. It's useless, we can do better, but it shows how it works. We can change the script to change the color, e.g., from red
to green
. To make Firefox reload any change, including changes to the manifest, click on the "Reload" button on the temporary extension panel.
"browser_action": {
"default_area": "navbar", #1
"default_icon": "icons/trello-tile.svg" #2
}
Let's start with the content-script
I used in the above manifest.json
. Content scripts are bound to a web page. As such, they can access its DOM. They run when Firefox loads the page. The script adds a red border around the web page's body
in the tutorial.
However, we need another kind of script: one to trigger when we click on the button. Such scripts should run along with the extension but can listen to events. They are known as background
scripts.
Background scripts are the place to put code that needs to maintain long-term state, or perform long-term operations, independently of the lifetime of any particular web pages or browser windows.Background scripts are loaded as soon as the extension is loaded and stay loaded until the extension is disabled or uninstalled, unless persistent is specified as false. You can use any of the WebExtension APIs in the script, as long as you have requested the necessary permissions.
--
Let's create such a script. It starts with the manifest
- as usual:
"background": {
"scripts": [ "background.js" ]
}
function foo() {
console.log('Hello from background')
}
browser.browserAction.onClicked.addListener(foo) //1
foo
function as an event listener to the button. When one clicks the extension button, it calls the foo
functionLet's stop for a moment to talk about debugging. I lost several hours because I didn't know what had happened. When I started to develop JavaScript 20 years ago, we "debugged" with alert()
. It was not the best developer experience you could hope for. More modern practices include logging and debugging. Spoiler: I didn't manage to get debugging working, so I'll focus on logging.
First things first, content scripts work in the context of the page. Hence, logging statements work in the regular console. Background scripts do work in another context. To watch their log statements, we need to have another Firefox developer console. You can open it on the extension panel by clicking the "Inspect" button.
Let's change the code a bit so that background.js
sends a message:
function sendMessage(tab) {
browser.tabs
.sendMessage(tab.id, 'message in from background')
.then(response => {
console.log(response)
})
.catch(error => {
console.error(`Error: ${error}`)
})
}
browser.browserAction.onClicked.addListener(sendMessage)
Now, we change the code of content.js
:
browser.runtime.onMessage.addListener((message, sender) => {
return Promise.resolve('message back from content')
});
So far, we have implemented a back-and-forth flow between the background
and the content
scripts. The meat is to get content from the page in the content
script and pass it back to the background
via a message. Remember that only the content
script can access the page! The code itself uses the Document API, e.g., document.querySelector()
, document.getElementsByClassName()
, etc. Specifics are unimportant.
"content_scripts" : [{
"matches": [ "//sessionize.com/*" ], #1
"js": [ #2
"content/common.js", #4
"content/sessionize.js"
]
},
{
"matches": [ "//www.papercall.io/*" ], #1
"js": [ #3
"content/common.js", #4
"content/papercall.js"
]
}]
At this point, we managed to get the necessary data and send it back to the background
script. The last step is to call Trello with the data.
We can configure a Firefox extension via a dedicated options page. To do so, the manifest offers a dedicated options_ui
section where we can provide the path to the HTML page:
"options_ui": {
"page": "settings/options.html"
}
function saveOptions(e) {
browser.storage.sync.set({ //1
listId: document.querySelector('#list-id').value,
key: document.querySelector('#key').value,
token: document.querySelector('#token').value,
})
}
function restoreOptions() {
browser.storage.sync.get() //1
.then(data => {
document.querySelector('#list-id').value = data.listId || ''
document.querySelector('#key').value = data.key || ''
document.querySelector('#token').value = data.token || ''
}, error => {
console.error(`Error: ${error}`)
})
}
document.addEventListener('DOMContentLoaded', restoreOptions) //2
document.querySelector('form').addEventListener('submit', saveOptions) //3
storage
APIform
We also need to ask the storage
permission in the manifest:
"permissions": [ "storage" ]
We can use the same storage
API in the Trello calling code to read credentials.
At this point, I was happy with my setup. I just added another round-trip from the background
to the content
to display an alert
with Trello's card name and URL.
To go further:
Originally published at on April 2nd, 2023