Bottom Line: One can write a Firefox extension in (mostly) Python via Pyodide.
With the recent amazing advancements in Python and wasm, brought to us in large part by way of Pyodide (repo) and PyScript (repo), I thought it would be interesting to try to build a Firefox extension in Python.
I found a very helpful Medium article and corresponding GitHub repo
for building a Chrome extension in Python, which provided some examples and a
framework. I wanted to do things a little differently (for no good reason) –
specifically, I didn’t want to rely on an
html-based pop-up page, which that
I struggled to get PyScript to work in the way I wanted, but I was eventually able to get Pyodide to help me create an extension that contains its own Python wasm runtime (and therefore doesn’t need to load it from their web-hosted version and should be a little snappier to load in some cases).
To try out the toy extension:
- clone the example repo, which is at https://github.com/n8henrie/python_firefox_ext.git
- inspect (for safety)
setup.sh(MacOS / probably Linux) or
setup.ps1(Windows) and afterwards run them; this will download the necessary files from Pyodide so you can embed them in your extension.
- You can also consider changing the script to download the debug version during development
- open Firefox to
- click the link for
Load Temporary Add-on...
- Select the
manifest.jsonfrom the cloned repo
In short, I found that you can import
pyodide.js in your
using a local path. That defines a function
loadPyodide, which can accept an
object with an
manifest.json then loads a local
hello.js, which calls
indexURL set to
a local path to the rest of the necessary files.
From here, loading and running some Python is a little janky, but seems to work
– I just read the contents of
hello.py and pass it (as a string) to
pyodide.runPython. One reason I wanted to structure things this way is it
allows me to use my usual Python workflow to write / edit / lint / format the
hello.py, I demonstrate very basic functionality for both a
content_script extension, which can modify the content one sees, as well as a
background extension, which has access to inspect, open, and close tabs (among
many other things). To demonstrate the
content_script functionality, the
extension sets a red border around the currently open webpage. In
manifest.json, I restrict the extension to only run this content script on
n8henrie.com, so if you open a page to my site you should see a red border.
For the background script functionality, I print out a list of currently open
tabs into the devtools console; to view this, click the
Inspect button in
about:debugging tab, then go to the
Console tab. I also print out
the current webpage’s URL, and open a new page to this blog post (which should
get a red border).