This tutorial will cover how to create a web extension that works across multiple browsers. It will show you how to structure a project and write JavaScript code to interact with the browser’s tabs, depending on which browser is being used. This means you can code and then distribute one extension package to multiple browsers’ web stores.
This post will focus on the Chrome and Firefox browsers, along with distributing extensions via the Chrome Web Store and Firefox Add-ons websites. Other browsers and distribution options are also available for web extensions.
Get started with a template — copy, edit and publish!
To see a completed example, here’s a link to an extension I made called Link Formatter, with the cross-browser JavaScript within popup.js. The same package is listed in both the Chrome and Firefox web stores.
Extensions are a fantastic way to extend the functionality of your browser, and they allow you to improve your experience online. If you are building your first one or want to learn more about them, I recommend the following tutorials:
When developing your extension, you can load it locally without having to publish and download it from an external website. The way you do this depends on which browser you are using.
Chrome
chrome://extensions/ in your Chrome browserLoad UnpackedFirefox
about:debuggingLoad Temporary Add-onmanifest.json within the extension’s folderDebugging tip: to view the console, (for example to see errors), right click/control click on the web extension icon or popup and select inspect
There are many JavaScript API’s that can be used in your browser extension. This post will focus on the tabs API.
For more on web extension APIs, see JavaScript APIs — Mozilla | MDN.
A browser extension that includes a popup HTML page when the user clicks on the icon in the browser’s toolbar could have a project structure like this:
extension
├── css
│ └── style.css
├── js
│ └── popup.js
├── manifest.json
└── popup.html
The popup.html page would then run the js/popup.js script at the bottom of the page. You would add your JavaScript here.
Note: other project structures could have a folder for library code, as well as JavaScript files that run in other areas of the extension. For example, in the extension’s background scripts, and in any other documents bundled with the extension, including browser action or page action popups, sidebars, options pages, or new tab pages.
When writing a web extension, you need to use the tabs API to interact with the tabs in the browser. You also need to request permission from the user to do this.
Permissions need to be set in manifest.json. When a user tries to install the extension, it will prompt the user to confirm that this action is allowed.
"permissions": [Firefox is one of the browsers that uses the browser.tabs API
"tabs"
]
Browsers, such as Firefox, use the browser.tabs API to interact with the browser’s tabs. To request info about the window’s tabs, you use the query method, which returns a promise with an array of tabs.
browser.tabs.query(
queryInfo // object
)
Read more about browser.tabs.query at tabs.query() — Mozilla | MDN
To request the active tab for the browser window, you would write the following JavaScript:
browser.tabs.query({active: true, currentWindow: true})
.then(logCurrentTabData)To get the current tab, you retrieve the first tab from the returned array of tabs. Following this structure, you can get the data from the browser tab.
const logCurrentTabData = (tabs) => {
currentTab = tabs[0]
console.log(currentTab.title);
console.log(currentTab.url);
}However, when you try to open the extension in Chrome…
Chrome has its own API for tabs queries. This follows the syntax chrome.tabs → your query
chrome.tabs.query(object queryInfo, function callback)
Read more about Chrome’s tabs API here: chrome.tabs — Google Chrome.
So to use this method call, you would write the following in your browser extension:
chrome.tabs.query(
{active: true, currentWindow: true},
(arrayOfTabs) => { logCurrentTabData(arrayOfTabs) }
);
You can then include both types of browser queries in your extension by using a conditional statement to determine which one to use.
if(chrome) {
chrome.tabs.query(
{active: true, currentWindow: true},
(arrayOfTabs) => { logCurrentTabData(arrayOfTabs) }
);
} else {
browser.tabs.query({active: true, currentWindow: true})
.then(logCurrentTabData)
}Within each side of the condition, you can then add other pieces of code that depend on the different APIs, for example to create new tabs.
chrome.tabs.create()
browser.tabs.create()
Here is the code with the extra methods added in that opens a link in a new tab.
if(chrome) {
chrome.tabs.query(
{active: true, currentWindow: true},
(arrayOfTabs) => { logCurrentTabData(arrayOfTabs) }
);
$('a').click( (event) => { chrome.tabs.create({url:event.target.href}) } )
} else {
browser.tabs.query({active: true, currentWindow: true})
.then(logCurrentTabData)
$('a').click( (event) => { browser.tabs.create({url:event.target.href}) } )
}With this code in place, you can now interact with the current browser without having to write two or more different web extension projects. You can now package your extension and publish to multiple web stores with the same file!