This is the second part of my blog series on browser extensions. Here, we’ll delve into advanced concepts including TypeScript integration, service workers, and programmatic script injection. For a solid foundation, I recommend reading Browser Extensions: Part 1 - Introduction before tackling these more complex topics.
How to use TypeScript in browser extensions
By default, browser extensions use JavaScript as the programming language in the content scripts. However, TypeScript is more type safe and reliable to write the business logic. You can add TypeScript support to the extension project, but how do you do it? Chrome docs mention this as a one-liner as follows:
If you are developing using a code editor such as VSCode or Atom, you can use the npm package chrome-types to take advantage of auto-completion for the Chrome API
OK, what exactly does this mean? All we know so far is that browser extensions have a simple structure with manifest file, HTML, CSS and JS files. How do we (in fact, can we) install an npm package in a browser extension? Also, this talks about auto-complete, but we need more than that.
Well, it turns out that it is not that difficult. It requires a few additional steps, but they are the same as adding TypeScript to any web project. Here are the steps to do it:
1. Initialize a Node.js project
- Open your terminal/command prompt
- Navigate to your extension’s root directory
- Run ’npm init -y’ to create package.json
2. Install the ‘chrome-types’ package
- Run command: npm install –save-dev chrome-types
- Now you have auto-complete for Chrome APIs
- If your tsconfig.json is showing the error “Cannot find type definition file for ‘chrome-types’”, then it means the npm package is not installed correctly.
- Run the command ’npm list chrome-types’ and verify that the package is listed.
- If not, check your tsconfig.json and make sure that the ‘compilerOptions’ is configured correctly.
3. Configure TypeScript
- Install TypeScript by installing the typescript package
- Run command: npm install –save-dev typescript
- Create a tsconfig.json file in your project root
|
|
4. Create your TypeScript files
- create your TypeScript file, for eg: hello.ts
- at the top of your TS file, add a special tag reference referring to chrome types
|
|
5. Update your HTML file to refer to the TypeScript file
- Make sure your manifest file, HTML files and content scripts point to the TypeScript files
|
|
That’s it! Now you can do all fancy stuff like this with the full auto complete in your VS Code, Web Storm etc. TypeScript away!
Service workers
Browser extensions provide a mechanism service workers to run scripts in the background. As the main even handler of the extension, these service workers run in the background and handle browser events like bookmark added, tab opened etc. An extension service worker is loaded and unloaded as needed.
Service workers don’t have access to the DOM, but extensions can utilize a hidden offscreen document for DOM-related tasks using the Chrome Offscreen API.
Let’s build an extension ‘Colorize’ that can change the background of a page to a random set of colors - like a beautiful rainbow! You can see the source code for this extension at annjose/browser-extensions/bg-colorizer-ext
- Build the manifest file
|
|
-
Create the popup html - this is straigtforward, so see the file in the git repo
-
Create the popup js
|
|
- Create the service worker file
|
|
- Load the extension from the Extensions page and enable the extension.
If everything works well, you will see a button ‘Colorize Page’ in the extension and when you click on it, the background color of the page in the active tab will change to a random color. As you continue to click on the button, the background color will change to different color. Have fun!
Troubleshooting
Error: “Error handling response: TypeError: Cannot read properties of undefined (reading ’executeScript’) at chrome-extension://befckjdncklbncigineablgcikdjcpfi/color-worker.js:27:24”
This happens when the manifest file does not have the permissions for scripting.
Solotion: add the following to the manifest.json
|
|
Error - “Uncaught (in promise) Error: The extensions gallery cannot be scripted.” This occurs when you are running the extension on a sensistiv page like Extension gallery like https://chromewebstore.google.com/ or https://microsoftedge.microsoft.com/addons/.
Solution - open the extension on another page like https://github.com/ and enjoy the color rainbow!
How to inject content script programatically
In the previous post, we looked at three ways of injecting content scripts into an extension - statically declare, dynamically declare and programmatically inject. It covered the first two methods in depth, so now let’s look at the third method.
Injecting content scripts programatically is useful when you want to run some script based on a condition or in response to specific events. You can inject the scripts using the Chrome Scripting API chrome.scripting.executeScript, which takes an array of script files (as ‘files’) or a function body (as ‘func’) and executes the script in the context of the extension.
|
|
|
|
|
|
|
|
Note that in order to inject a script programatically, you need to specify the host permisions for the page you are trying to inject the file. Or you can specify active_tab as specified below.
Other concepts
Toolbar action
Executes code when the user clicks on the extension button in the browser toolbar. Use the Chrome API chrome.action
Side Panel
Hosts content in the browser’s side panel alongside the main content of the web page. Use API chrome.sidePanel
DeclarativeNetRequest
Allows you to intercept, block or modify a network request.
References
- Chrome Documentation
- Part 1 of this article - Browser Extensions: Part 1 - Introduction