Important note to begin with: A page can't query if an extension is installed without explicit help from the extension. This is done to prevent browser fingerprinting and/or preventing sites from denying content if certain extensions are installed.
WebExtensions are largely built upon the same principles as Chrome extensions. As such, this question is relevant: Check whether user has a Chrome extension installed.
However, some of the best methods available in Chrome are currently unavailable in Firefox:
The files will then be available using a URL like:
moz-extension://<random-UUID>/<path/to/resource>
This UUID is randomly generated for every browser instance and is not your extension's ID. This prevents websites from fingerprinting the extensions a user has installed.
As such, what are your options? The page can't talk directly to the extension context (background), and the background can't directly affect the page; you need a Content script to interact with the page content.
How can page code and a content script communicate? They are isolated from each other unless content script does something about it.
First off, generic tricks that work in both FF and Chrome:
You can create or modify a DOM element on the page from a content script and look for those modifications in the page.
// Content script
let beacon = document.createElement("div");
beacon.classname = browser.runtime.id;
document.body.appendChild(beacon);
// Page script
// Make sure this runs after the extension code
if (document.getElementsByClassName("expected-extension-id").length) {
// Installed
} else {
// Not installed
}
You can use postMessage
to communicate between contexts, though it's clunky to use as a bidirectional channel.
Here's documentation and sample WebExtension.
// Content script code
window.postMessage({
direction: "from-content-script",
message: "Message from extension"
}, "*");
// Page code
window.addEventListener("message", function(event) {
if (event.source == window &&
event.data.direction &&
event.data.direction == "from-content-script") {
// Assume extension is now installed
}
});
You can use custom DOM events in a similar way.
There are interesting Firefox-specific approaches as well:
You can share code with the page using exportFunction
or cloneInto
:
// Content script
function usefulFunction() {
/* ... */
}
const extensionInterface = {
usefulFunction
}
window.wrappedJSObject.extensionInterface =
cloneInto(extensionInterface, window, {cloneFunctions: true});
// Page code
if (typeof window.extensionInterface !== "undefined") {
// Installed
window.extensionInterface.usefulFunction();
} else {
// Not installed
}
window.my-ext-name-a1b2c34d = true
in your extention, and then checkif (window.my-ext-name-a1b2c34d)
on your website – Companionexternally_connectable
), it's worth noting it here. – Cortez