Create your first Chrome extension

Anna
Active Developement
5 min readFeb 13, 2023

A step-by-step tutorial to create your first extension in 15 minutes flat !

The purpose of this extension will be to add a button to your Chrome toolbar. On click, it will reload all the CSS files present on the current opened page.

So let’s get started !

1. Create the extension skeleton

Create a folder on your computer that will contain the project.
for example : css-reload/

Inside, add a new file named : manifest.json

This file is our entry point for interacting with Chrome. It contains all the information needed to display the icon in the browser, changed permissions, and other metadatas….
Let’s start by adding the mandatory information for all extensions :

We specify the version of the manifest that we will use, here version 3.
We add a title for our extension and its version.

{ 
"manifest_version": 3,
"name": "Reload CSS",
"version": "1.0.0"
}

2. Tell Chrome about it

(cf. chrome “Getting Started Guides”)

  1. On Chrome, got to the URL : chrome://extensions/
  2. Enable Developer mode
  3. Click on the Load unpacked button
  4. Find your project directory — here css-reload/

Your extension should now appear in the extension menu and you can already pin it to your toolbar.

3. More configuration

At the moment, your extension does nothing at all.
Let’s modify our manifest.json file a bit.

  • Define the behavior of our icon in the toolbar :
    For our current application, it is possible to leave it empty but the ‘action’ key must be present.
"action": {
"default_title": "Reload Css for this tab"
}
  • We add the permissions that allow us to run scripts and have access to the current tab :
"permissions": ["activeTab", "scripting"]
  • We declare the script that will be called :
"background": {
"service_worker": "background.js"
}
  • And to do things well, we add a small logo for our extension :
"icons": {
"512": "images/icon-512.png"
}

Which leaves us with a manifest file like this…

{
"manifest_version": 3,
"name": "Reload CSS",
"version": "1.0.0",
"icons": {
"512": "images/icon-512.png"
},
"action": {},
"permissions": ["activeTab", "scripting"],
"background": {
"service_worker": "background.js"
}
}

4. Let’s do magic…

Create a background.js file :

chrome.action.onClicked.addListener((tab) => { 
chrome.tabs.reload(tab.id); // simply reload the current tab
});

The idea here is to create a listener for when we click on our extension.
Here, we simply reload the current tab.

We want to run javascript in tab context, so we need ‘activeTab’ and ‘scripting’ permissions. Note, for security reasons, Chrome blocks the execution of scripts on its configuration pages. We will therefore filter these URLs.

// we create a listener on our extension button 
chrome.action.onClicked.addListener((tab) => {

// for security reason, "chrome://" urls are blocked so we skip them
if(!tab.url.includes("chrome://")) {

// we call our script in the current tab
chrome.scripting.executeScript({
target: { tabId: tab.id },
args: [tab.title],
function: (tabTitle) => console.log(`I'm clicked on tab "${tabTitle}"`),
});
}
});

At this level, you should now have a message displayed in the console when you click on the extension icon.

5. One button to reload them all !

In a scripts/reload.js file :

(function() {

// the reload function
const reloadCss = function(el) {
if(el.rel.toLowerCase().indexOf('stylesheet') >= 0 && el.href){

// get target url for the link element
const h = el.href.replace(/(&|%5C?)forceReload=\d+/,"");

// force the reload by appending current date
el.href = h + (h.indexOf("?") >= 0 ? "&" : "?") + "forceReload="+(new Date().valueOf());
}
};

// reload each <link> css elements
Array.from(document.getElementsByTagName("link")).forEach( el => reloadCss(el) );

// we do the same for each iframe element we found
Array.from(document.getElementsByTagName("iframe")).forEach( ifr => {
Array.from(ifr.contentDocument.getElementsByTagName("link")).forEach( el => reloadCss(el) );
});
})()

Call this script in our previousbackground.js file :

// we create a listener on our extension button
chrome.action.onClicked.addListener((tab) => {

// for security reason, chrome:// urls are blocked so we skip them
if(!tab.url.includes("chrome://")) {

// we call our script in the current tab
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['scripts/reload.js']
});
}
});

And that’s all ! Our extension is now able to reload the CSS of our page.

6. We need some options

To go further, we can imagine that we want to be able to activate / deactivate the reload of the css inside the iframes.
For this, we will need a configuration page.
This page will need access to the storage api, so we also need to modify the permissions of the extension.

In manifest.json , add 2 lines :

"options_page": "options/options.html",
"permissions": ["activeTab", "scripting", "storage"],

options/options.html

<!DOCTYPE html>
<html>
<head>
<title>Reload CSS Options</title>
</head>

<body>
<label>
<input type="checkbox" id="iframes"> Reload iframes
</label>

<br><br>
<hr>
<br>

<div id="status"></div>
<button id="save">Save</button>

<script src="options.js"></script>
</body>
</html>

options/options.js

// Saves options in Chrome's storage
function save_options() {
const reloadIframe = document.getElementById('iframes').checked;
chrome.storage.sync.set({
reloadIframe: reloadIframe
}, function() {
// Update status to let user know options were saved.
var status = document.getElementById('status');
status.textContent = 'Options saved.';
setTimeout(function() {
status.textContent = '';
}, 750);
});
}
document.getElementById('save').addEventListener('click', save_options);

// Restores select box and checkbox state using the preferences stored in chrome.storage.
function restore_options() {
// Use default value reloadIframe = true.
chrome.storage.sync.get({
reloadIframe: true
}, function(items) {
document.getElementById('iframes').checked = items.reloadIframe;
});
}

document.addEventListener('DOMContentLoaded', restore_options);

We also need to change our call to the script to pass our configuration as arguments :

// we create a listener on our extension button
chrome.action.onClicked.addListener((tab) => {

// for security reason, chrome:// urls are blocked, so we skip them
if(!tab.url.includes("chrome://")) {

//load our extension configuration
chrome.storage.sync.get({reloadIframe: true}, function(options) {

// little trick to call our script in the current tab with arguments
chrome.scripting.executeScript({
target: {tabId: tab.id},
args: [options.reloadIframe],
function: reloadIframe => Object.assign(self, {reloadIframe}), // we add our arguments in the 'self' scope like globally declared vars
}, () => chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['scripts/reload.js']
}));
});
}
});

Finally, we can use our configuration to choose whether to reload the iframes or not :

(function() {
const reloadCss = function(el) {
if(el.rel.toLowerCase().indexOf('stylesheet') >= 0 && el.href){
const h = el.href.replace(/(&|%5C?)forceReload=\d+/,""); // get target url for the link element
el.href = h + (h.indexOf("?") >= 0 ? "&" : "?") + "forceReload="+(new Date().valueOf()); // force the reload by appending current date
}
};

// reload each <link> css elements
Array.from(document.getElementsByTagName("link")).forEach( el => reloadCss(el) );

// use the saved configuration
if( reloadIframe ?? true ) {
// we do the same for each iframe element we found
Array.from(document.getElementsByTagName("iframe")).forEach( ifr => {
Array.from(ifr.contentDocument.getElementsByTagName("link")).forEach( el => reloadCss(el) );
});
}
})()

--

--