But what's a plain Web Extension?
This article is part of a series.
Safari Web Extensions envelop a ball of HTML, javascript, CSS and JSON in an Xcodeproj and shove it into Safari. That same ball can be shoved into Firefox and Chrome… sort of. It has the same cross-browser issues that all other web development does and has always had.
This post covers references on how to check an extension in Firefox and what’s in the manifest.json
References
-
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions
-
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions
Being Cross Compatible
- https://extensionworkshop.com/documentation/develop/browser-compatibility/
- https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Build_a_cross_browser_extension
When reading about chrome extensions keep in mind that firefox and safari use browser:
among other difference
Checking Extension in Firefox
More info: https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/
brew install web-ext #or npm
web-ext --version
cd /PATH/TO/StandaloneExtension
web-ext run
See also about typing “about:debugging” and “about:addons” into the FireFox toolbar.
What kinds of things can a Browser Extension do?
Finding out what a web extension does starts with reading its manifest.json Good news, a manifest.json can have //
comments!
Here are some things you might find in a V3 one:
Extension Identity
Start with the basic meta information. The minimum viable manifest.json
comprises of just. manifest_version
, version
, and name
.
"manifest_version": 3,
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "1.0",
"homepage_url": "https://example.com",
If you see __MSG_extension_name__
look for a default_locale
and _locales
folder because that’s internationalization and localization variable. More info on i18n conventions
Icon images can be a collection of pngs or a single svg.
"icons": {
"512":"images/icon-512.svg"
},
"icons": {
"48": "images/icon-48.png",
"96": "images/icon-96.png",
"512": "images/icon-512.png",
"1024": "images/icon-1024.png"
},
Background Processes
Background processes happen behind the scenes, but are not allowed to keep running indefinitely. Chrome and safari prefer the service-worker API if available. Firefox falls back to the non-persistent background script.
"background": {
"service_worker": "background.js",
"scripts" : ["background.js"],
"persistent" : false
},
Background processes frequently get paired with storage or [unlimitedStorage][prm_ulm] permission, which then take advantage of storage.local, storage.managed or storage.session.
Content Scripts
Content scripts have a more constrained range of abilities than background scripts, but they run inside a loaded page’s DOM.
"content_scripts": [{
"js": [ "content.js" ],
"matches": [ "*://*.whynotestflight.com/*" ]
}],
Several match patterns can be used to dial in what websites the script should try to load itself into, including <all_urls>
.
Tool Bar Actions
Add an item to the browser tool bars with an action
"action": {
"default_popup": "popup.html",
"default_icon": {
"512": "images/toolbar-icon-512.svg"
}
},
Custom Browser Pages
Replace new tab page or others. Comparable to chrome_url_overrides
"browser_url_overrides": {
"newtab": "new_tab_page.html"
},
Content Blockers
declarative_net_request controls how network requests get handled and is the basis for content blockers.
"declarative_net_request": {
"rule_resources": [
{
"id": "MyRuleset",
"enabled": true,
"path": "rules.json"
}
]
},
Example rules.json that blocks all images on all websites. Rule sets can be static (life time of extension) or dynaimc (session conditional). more
[
{
"id" : 1,
"priority" : 1,
"action" : { "type" : "block" },
"condition" :
{
"urlFilter" : "*.",
"resourceTypes" : [ "image" ]
}
}
]
Controlling Abilities
An extension writer can request more or less abilities for their extension. Most browsers will then let the installer know how much more the extension wants to do.
Permissions
An extension can ask for install time permissions(permissions) or runtime permissions(optional_permission, a subset). The desired permissions get listed in an array.
"permissions": [ "activeTab", "storage", "scripting" ],
Some common permissions:
- “activeTab”: to inject scripts into the DOM, retrieve information like the URL for the active tab only.
- “scripting”: needed with activeTab to actually do the injecting.
- “storage”: use the storage apis (see background)
- “tabs”: interact with all the tabs and the tab system.
- “declarativeNetRequest”: to use them
- “nativeMessaging”: to send messages to and from the extension’s delivery shell, the common use case is password managers.
There are a handful of non-api permissions (like activeTab and [clipboardWrite]), but the bulk are about fine-grained dialing in of what parts of the web extensions API the extension actually needs.
Content Security Policy
content_security_policy tightens or loosens what scripts from where can do. For example, the below would enable the use of web assembly.
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'"
}
Manifest V3 gives a lot less latitude to extensions drawing in scripts the installer might not be able to reason about (remote, dynamic, etc) For example, it only allows ’none’, ‘self’, and ‘wasm-unsafe-eval’ as script source parameters. The default policy for V3:
"script-src 'self'; upgrade-insecure-requests;"
In general terms:
- Only local scripts and images.
- No eval, i.e. reading in strings on the fly as JavaScript.
- No JavaScript embedded in HTML tags.
- No WebAssembly unless explicitly asked for.
- http gets punted to https automatically.
More details from Mozilla from W3C
Externally Connectable
“Externally connectable controls which other extensions and web pages can communicate with an extension using runtime.connect() and runtime.sendMessage() message passing. If externally_connectable is not specified, all extensions can communicate with each other but not with web pages.” page
"externally_connectable": {
"matches": [ "*://*.example.com/*" ]
},
Load Resources
web_accessible_resources determines what resources an extension can use depending on what page. Below example lets the script load files with .xml
and .xsl
extensions from a directory “xsl” in the extension’s root when the page in the activeTab has the .xml
extension.
"web_accessible_resources": [
{
"resources" : ["xsl/*.xml","xsl/*.xsl"],
"matches": ["*://*/*.xml"]
}
]
Summary
This catalogs a lot of the things that can be controlled, next post, how to use them.