We don’t have it in the store yet, so we are going to do this the manual way. We will follow the first approach where we will load the “unpacked” version of our work ,by indicating to google where the folder with our files is.

Navigate to chrome://extension on your Google Chrome.

From there, turn on developer mode (switch in the top right corner).

Click on the “Load unpacked” button in the top right corner.

Select the folder containing your 2 files (plus an image for the icon).

If all goes well, you should be seeing your plugin both in the main page and on the toolbar.

Now, if you click on the toolbar icon you should be seeing the HTML we have added:

Congrats! You have successfully created a small plugin! Now you might think that it’s just a “Hello world”, but don’t be fooled. This is a fully functional website. You can do everything you would normally do in a typical website. You can make the index.html load javascript files, css files, fonts, etc. You can create a React application inside it if you wanted. Literally, anything! One thing to know though, is that each time you close it, it’s as if you have closed the tab of a website. Everything that was stored in memory gets lost, just like a website would do after a refresh. After all, it is a website!

So why would we want to have a popup in the first place? Well it’s mainly used as a UI for your users. It provides a nice way for a plugin to interact with its users so they can give input/feedback on certain things. Interestingly enough, the core functionality of the plugin is not normally stored there, since that’s something for the content scripts or the background scripts to handle.

Content scripts

These are the CSS and JS files that get added on top of a website. That means that you can add extra files to a website as if they were added by the original developers themselves. Have you ever seen how some extensions manipulate the things you see in a page? That’s how they do it! They simply add additional Javascript and/or CSS to it. You can add as many extra files as you want, as long as you specify them in the manifest.

To showcase their powers, we’ll tweak our previous example and give it some additional functionality. What doesn’t seem like fun, is to change the background colour of all of google’s websites to red, so let’s do just that. Create a content.css file and add the following code to it:

body {

background-color: red !important;

}

In addition, why not write some javascript as well? Let’s create a content.js file and let’s make sure that each time the user clicks anywhere in the page, an alert shows up:

document.addEventListener('click', () => alert('Click occurred!'));

Finally, let’s register both of these files as content scripts. To do that, we need to specify to which websites will these scripts be “added to”. This is done through the “matches” key, which is a regex pattern and can allow you to target any website, from every single one of them to only particular ones. In our example, we want to register our content scripts to all websites that end with google.com. To do that, let’s change our manifest.json and add the following code:

{

"name": "My plugin",

"version": "0.0.1",

"description": "This is the full description of the plugin",

"manifest_version": 2,

"browser_action": {

"default_popup": "./index.html",

"default_title": "Open the popup",

"default_icon":"./image.png"

},

"content_scripts": [

{

"js": ["content.js"],

"css": ["content.css"],

"matches": ["https://*.google.com/*"]

}

]

}

Reload the plugin by clicking on the refresh icon in the plugin card, found under chrome://extensions :

Refreshing the plugin’s data after an update on the manifest

Now, try opening google.com. You should be seeing something like this:

modifications of CSS content script

If you attempt to click anywhere, you should see a horrible alert popping up:

modiciations of the JS content script

It’s stupid, I know, but see the big picture here. You can add anything you wish, to any website you wish. You can mount a React app on a DOM element of a website and run your own React application alongside the existing website! These scripts are loaded as soon as the websites are opened and remain active up until their corresponding tabs close. Think of them exactly like normal scripts that the website would load by itself.

Background Scripts

Let’s forget for a minute the dummy extension that we are working on and let’s say we wanted to sync all the files that the user has downloaded to a remote repo. We would need a script that continuously monitors Chrome’s downloads, but where would we put it? We wouldn’t put it in the content scripts since it would only load when a particular page loaded. We also wouldn’t put it in the popup since its scripts only run when you open the popup and stop running as soon as you close the popup. Well, that’s a job for the background scripts.

These are JS scripts that run outside the context of a page but within the context of a browser. They are activated once when you install the plugin and — as long as the plugin remains installed — remain active as long as they have at least one (1) listener registered. The only way a background script would stop running is if it literally doesn’t have anything to do. If you instruct it to monitor or listen for something, it will always be up & running in the background, patiently waiting... Ugh, that’s kinda creepy.

The background scripts are the most stable pieces of the Chrome Extension ecosystem when it comes to logging stuff & communicating with your server or API, because they are not prone to “sudden kills”. Content scripts and scripts inside the popup have a lifecycle that depends upon the website and popup accordingly. On the contrary, background scripts depend upon your extension, so as long as it remains installed, then the scripts will be running in the background in a daemon fashion.

To dip our toes in the water, let’s go ahead and create a file background.js and add a simple alert to it:

alert('Hello from background script!');

Now let’s make sure we register it in our extension as a background script by tweaking the manifest.json as seen below:

{

"name": "My plugin",

"version": "0.0.1",

"description": "This is the full description of the plugin",

"manifest_version": 2,

"browser_action": {

"default_popup": "./index.html",

"default_title": "Open the popup",

"default_icon":"./image.png"

},

"content_scripts": [

{

"js": ["content.js"],

"css": ["content.css"],

"matches": ["https://*.google.com/*"]

}

],

"background": {

"scripts": ["background.js"]

}

}

Now, if we refresh the plugin (like in the previous example), you should be instantly greeted by an alert.

Activation of the background script

As we mentioned before, the background script is activated once, as soon as you install your extension. If you deactivate & reactivate the plugin (through the switch that is visible in the screenshot above), you will see this alert again.

Putting it all together

As mentioned before we have three (3) pieces to our ecosystem; the popup, the content scripts and the background scripts. Let’s make sure we got it right:

Popup: The custom UI for the plugin. You can define HTML, CSS, JS, Images related to the popup. It behaves exactly like a typical website. Whenever you toggle the popup, it’s as if you were refreshing a website! (i.e. data gets reseted)

The custom UI for the plugin. You can define HTML, CSS, JS, Images related to the popup. It behaves exactly like a typical website. Whenever you toggle the popup, it’s as if you were refreshing a website! (i.e. data gets reseted) Background Scripts: A collection of JS scripts that run in the background once per extension installation . Useful for configuration & setup that we only declare once & don’t want to ever close or reset (global listeners, etc.).

A collection of JS scripts that run in the background once per extension installation Useful for configuration & setup that we only declare once & don’t want to ever close or reset (global listeners, etc.). Content Scripts: A collection of scripts that get added to a website’s code, as if they were shipped with the website itself. Has access to everything in the DOM. Useful for interacting with the DOM of a page in any sort of way.

A plugin must register at least one of those, else it had no reason to be a plugin to begin with. Even though all of these pieces are optional (for example you might not ever need a popup cause you don’t want to expose any interface), it’s still mandatory to declare at least one (1) of them.

All of these three (3) individual pieces live in their own little isolated worlds. They are fully sandboxed, have no knowledge of one another and, as a result, cannot directly access each other. Instead, they rely on a set of APIs defined by Chrome in order to exchange information. You may have seen that before if you have worked with web workers, where the main & worker thread communicate with each other through messages. There are a ton of APIs (63 to be exact) and we won’t be able to cover all of them, but I’ll make sure I go through the core ones, since I rarely find myself using any other unless I’m building something extremely specific.

Chrome APIs

As mentioned right above, they provide a way for your extension to ask Chrome to do stuff for you. You can ask about the users’ tabs, get access to their downloads, to their browsing history, etc. With some exceptions, each API is coupled with a particular permission. Permissions are registered in the manifest.json and are a way of telling the user “Hey my plugin is going to have access to this, this and that”. It’s similar to how native apps work, where a lot of them will require access to a certain API — like the camera — when you download them. If you don’t specify the permission for a certain API, Google will now allow you to use it.

Messaging (Runtime) API

This is broad spectrum that contains all the methods used for the exchange of messages within the extension ecosystem and doesn’t require any permissions. There are currently two (2) different ways to send a message from one part of an extension to another:

One-time message requests (imagine them like an HTTP request) Long-lived connections (imagine them like a websocket)

The first one sends a one-time message to any target that you want, similar to an event being fired in the DOM. For each request message there can be an optional response message, so that’s why I’m correlating that to your typical HTTP request.

// send a message

chrome.runtime.sendMessage({ greeting: 'hello '}, response =>

console.log(response.farewell)

}); // respond to a message

chrome.runtime.onMessage.addListener(request,sender, sendResponse =>

if (request.greeting === 'hello') {

sendResponse({ farewell: 'goodbye' })

}

});

Unfortunately, if you are spamming messages, then it’s not very efficient to go this route. Instead, you pick the second option by creating a port that you can freely spam messages to. It can handle lots of traffic, but unfortunately it doesn’t provide a mechanism for responding to a message. Thus if you are dependent upon a response, then the first option would be the way to go.

// connect to a custom channel/port

const port = chrome.runtime.connect({ port: 'foo' }); // push a message to the channel

port.sendMessage({ greeting: 'hello' }); // react to the message (can't respond back!)

port.onMessage.addListener(request => {

if (request.greeting === 'hello') {

console.log('received a hello message');

}

});

When targeting a background script or a popup, then the chrome.runtime will do just fine. You can find all the info you might need in its documentation page.Unfortunately, if you are targeting a content script, then you need to specify which tab do you want to receive this message. In order to find the tab that you want (i.e. the one that contained “google.com” in our previous example), you must utilise the tabs API.

Tabs API