Notes on Writing a Web-Extension for Firefox
Using React
Don’t use create-react-app
if the extension uses a background script.
Trying to shoehorn multiple entry points and other configurations needed for writing a web-extension using something like craco
is not worth the effort.
Instead write a webpack config with multiple entry points.
And to add a plugin for a specific entry point use the chunks
option for the plugin if available.
You’ll likely want to use HtmlWebpackPlugin
, CopyWebpackPlugin
, ProvidePlugin
, and (maybe) ReactRefreshWebpackPlugin
.
Debugging Extension Pages with React Developer Tools (You Cannot)
React Developer Tools
does not have permission to access moz-extenison
pages and it may not be possible to change that.
Modifying the manifest to include <all_urls>
or moz-extension://*/*
under permissions
does not work either.
The error message when adding the moz-extension
scheme states this is an invalid option and that it ... must either [match the pattern /^(https?|wss?|file|ftp|\*):\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$/, or match the pattern /^file:\/\/\/.*$/], or match the pattern /^resource:\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^about:/]
.
On Installed (and Updated)
onNotInstalled?
The Web-Extenison API provides a runtime.onInstalled
event which is fired when the extension has been installed for the first time or has been updated.
This seems pretty handy at first for triggering first run logic however since there is no guaranteed way to know when this event has not fired it’s not very useful for creating first run logic which must run before the extension is initialized.
So what to do?
The storage.local
API can help out here.
Since the extension’s local storage will be wiped on extension removal and will be empty on extension install we can safely assume if the storage is empty this is the first run.
And by writing the extension’s version number to storage this same check can determine whether this is the first run after an update.
Event not Firing?
When registering the runtime.onInstalled
make sure to call it in the main loop and not in an async function call as you may miss the event.
I missed the event consistently in Firefox Developer Edition - 104.0b10 (64-bit)
when the event handler was registered in an async function call event when it was the first command.
Window ID and Tab ID Persistence
In Firefox Developer Edition - 105.0b9 (64-bit)
window and tab IDs are unstable when:
- the browser restarts
- when a window is reopened (tab and window IDs)
- when a tab is reopened
- when a tab is moved to another window
Tracking windows and tabs across these ID changes can be done generically using sessions.setWindowValue
and sessions.setTabValue
respectively to set an extension specific persistent ID.
In the move case it may be sufficient to rely on event callbacks like tabs.onDetached
and tabs.onAttached
.
Window and Tab Data not Cleared on Removal
I have observed in Firefox Developer Edition - 104.0b10 (64-bit)
that window and tab session data (sessions.setWindowValue
and sessions.setTabValue
) is not cleared on extension removal.
Since there is no runtime.onRemoved
event the next best thing is to clear all tab and window data on first run if the extension will be bothered by old data.
Tab Session Data Disappearing on Move
When a tab is moved from one window to another it looses the session data written using sessions.setTabValue
.
You’ll need to rewrite the data after you move the tab.
tabs.captureTab
Returns Images with Different Dimensions for Active and Inactive Tabs
In Firefox Developer Edition - 116.0b2 (64-bit)
calling browser.tabs.captureTab
with an unset rect
in the ImageDetails
param returns an image with dimensions of the visible viewport.
Sometimes (ex: when switching to and from a new tab with the bookmarks toolbar conditionally showing) the viewport height appears dependent on whether the tab is active or not.
(This can be observed by querying window.innerHeight
in a popped out dev console once with the tab active and again with the tab inactive. Or by querying browser.tabs.get
and checking .height
when the tab is active and again when inactive.)
When a tab is inactive the viewport height shrinks by 28 pixels.
I believe this is related to the bookmark bar conditionally being shown on a new tab however I have not dug into this further.