Part 3: Seamless script injection for Bitcoin Wallets - nearly 1ms
In the previous post, we saw how we can use the power of inlining and injecting scripts in the node environment as well as the browser environment. but this works only with Manifest-Version (MV2) extensions.
MV3 extensions have some higher content security policy restrictions which don’t allow inline injection anymore.
In this post, we will discuss how we can achieve provider speed on MV3 extensions
Introduction to the execution world
The "execution environment" refers to the specific context in which a script is run, and it can be controlled when using functions like scripting.executeScript()
or when registering scripts with scripting.registerContentScripts()
. This environment determines how the script interacts with the webpage and its available APIs.
The "ISOLATED" environment is the default for content scripts. In this context, scripts run in isolation from the main page's context. While content scripts share the same document as the main page, they have separate global scopes and access to APIs. This isolation helps maintain security and prevents interference between the main page and the script.
The "MAIN" environment is the execution context of the web page itself. Scripts running in this environment share the same context as the webpage and have access to its APIs. However, scripts in the "MAIN" environment don't have access to APIs that are exclusively available to content scripts. This distinction ensures that scripts running within the webpage's context do not have the same level of isolation as content scripts.
Using Main execution world injection to achieve faster speed
const registerInPageContentScript = async () => {
try {
await chrome.scripting.registerContentScripts([
{
id: "inpage",
matches: ["file://*/*", "http://*/*", "https://*/*"],
js: ["js/inpageScript.bundle.js"],
runAt: "document_start",
world: "MAIN",
},
]);
} catch (err) {
console.warn(`Dropped attempt to register inpage content script. ${err}`);
}
};
const manifest = browser.runtime.getManifest();
const isMv3 = manifest.manifest_version === 3;
if (isMv3) registerInPageContentScript()
This code snippet registers an in-page content script using the chrome.scripting.registerContentScripts()
API. The script is registered based on specific conditions and follows the WebExtensions API used in Chrome extensions.
The function registerInPageContentScript()
is asynchronous and tries to register a content script. It specifies the following details for the content script:
id
: A unique identifier for the script.matches
: An array of URL patterns to match against web pages where the script should run.js
: An array of JavaScript file paths to be injected into the web pages.runAt
: Specifies when the script should be executed, in this case, at the start of document parsing.world
: Specifies the execution environment of the script, which is set to "MAIN" (the webpage's context).
The code then checks whether the browser extension follows the Manifest V3 structure (MV3) by comparing the manifest_version
property. If the extension is using MV3, it calls the registerInPageContentScript()
function to register the content script, allowing it to run within the main execution environment of the web page.
This code is part of an extension's background script and demonstrates how to conditionally register content scripts based on the extension's manifest version. The example illustrates the flexibility and compatibility considerations when working with different versions of the Chrome extension API.
Doing Main world injection via Manifest vs Service Worker
Main-world injection can be done via manifest as well. but we didn’t notice a huge speed difference there. but when we did it via the service worker of the extension we find tremendous speed with this method.
The key point here is doing main world injection + doing it via service worker rather than doing it with manifest