Making a google chrome extension

Erwin Carrasquilla
Sep 4, 2018 · 10 min read

Chrome extensions are some of the most useful applications on the web. They are small and serve minimal purposes to help make web browsing, developing, and overall web life easier. There are chrome extensions that take screenshots, chrome extensions that help you organize your tabs, and other extensions that help with React, Angular, Vue JS, and other stuff that you may be able to use to make your life easier. But how do these extensions work? Well let’s find out by making a simple extension. This tutorial assumes that you have a basic understanding of JavaScript, HTML, and CSS.



For this tutorial we are going to create:

A chrome extension that replaces images that are hovered over with images of cats/dogs/Nicolas Cage. The choice is yours! Some things we want to keep in mind are that we want to change the picture when our mouse hovers over the image in the web page. When we move the mouse off the image it should return the image to it’s original order.


What is an extension

Extensions are small software applications used to enhance the chrome experience. These extensions can have event based logic, add options to the menu that appears after a right click, block ads, and be used to modify or enhance the chrome experience. They serve a single purpose and can have multiple pieces to them but all these pieces benefit a common purpose. Imagine having an extension that grabs a selected word and displays the definition of the selected word immediately under it. Or maybe an extension that allows for multi-cursor selection. Maybe you have one that gathers how many words per minute you type in chrome. Another good one is an extension that connects to Facebook and displays your messages and notification where ever you are in chrome. The possibilities can be endless, the only reason is because extensions listen in on what you are doing on a web page.

Extensions are made up of many different pieces. There can be background scripts that listen to events and execute some logic based on the conditions that are met, content scripts are used to read and modify the DOM visited and executes some Javascript , an options page that determines — well you know — the options , UI elements that can aid in creating popup interfaces , and various logic files that help with getting the core functionality implemented. Chrome extensions use JavaScript, HMTL, and CSS to create what we see and use in the browser.


Where to start?!

To start creating your first chrome extension masterpiece, you need a manifest.json. Hmm, I’ve heard of package.json…what the heck is the manifest.json?!

The manifest.json is similar to the well known package.json in JavaScript. It is required for each extension and defines some important information that said extension requires. Both the package.json and the manifest.json define the name of the project you are working on, the version, description, and other pieces of what makes your extension work. The manifest_version is special to a chrome extension, at least it’s the first time I have seen it so far. Currently the docs specify that the manifest version is 2.

// manifest.json{ 
“name”: “Replace with kitty”,
“version”: “1.0”,
“description”: “Build an Extension that replaces your images with kittens”,
“manifest_version”: 2
}

The above code would be the minimum required to be able to add this as a chrome extension to your browser. This code does absolutely nothing and is pretty self explanatory but we can still make it an extension that is useable within our local chrome browser. All we have to do is switch our chrome extensions to developer mode and add the extension to the browser. To add this project as an extension all you have to do is go to the chrome extensions page.

This is the chrome://extensions page

Then from here we have to enable developer mode.

Red arrow is pointing to switch for developer mode

Press on load unpacked and locate the extensions root file.

Red arrow is pointing to LOAD UNPACKED

The manifest can also take other options such as background scripts, permissions, and page_action. We will be using these for our tutorial but there are many different options that you can add to the manifest. Background scripts defining the scripts that get executed when and event being listened for is heard. The background script also goes idle allowing for it to not be loaded into memory at all times. Permissions just allow us to use the chrome API’s and gain access to certain functionality inside of the extension. Page action is not what you might think, this is just the button that represents the extension to the right of the url bar. All the icons to the right here are all page_actions available for this page.

We did not put in an icon or image to represent our extensions so google will default to it’s grey scale “G” logo which you can see all the way to the right. I personally do not want to add an icon to this tutorial app. If you would like to add an icon to your google chrome extension here is how you do it, it’s pretty simple.

Defining permissions

I believe that figuring out what permissions you need to start off with and defining them in the manifest first is an important step. So let’s start off with giving our extension a name and defining the activeTab and declarativeContent permissions.

// manifest.json
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"manifest_version": 2,
// This page_action option uses the activeTab permission
"page_action": {
"default_title": "My first extension"
},
"permissions": [
"activeTab",
"declarativeContent" // you can add whatever you want/need
]
}

The other approach , adding permission as you need them, can be used I typically do my research first to get an MVP and then tweak as necessary. For our extension we are going to need to have access to the active tab permission in order to gain temporary access to the currently active tab when the function is invoked. This will also allow us to make use of methods such as tabs.executeScript or tabs.insertCss. We could also grab the tabs information such as the url, title, and favicon as long as we are on that tab. The active tab permission can be used in conjunction with the declarative content permission. When we add the declarative content permission we then have the ability to show the page action depending on the URL of the web page and the CSS selectors that it’s styling contains. For this to happen there is no need to inject a content script into the page. Using both of these permissions allow us to interact with the web page after we click our page action. (The button by the url bar). If you would like to delve deeper into the rabbit hole here are the other permissions that you could apply to your extensions.

Tell your extension what to do!

This is where the background script will come in handy. The background script monitors events such as navigating to a new page, adding/removing a bookmark, switching tabs. It is loaded when it is needed and silenced when it is not needed. Our first step is to add it to the manifest.

// manifest.json
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"manifest_version": 2,
// This page_action option uses the activeTab permission
"page_action": {
"default_title": "My first extension"
},
"permissions": [
"activeTab",
"declarativeContent" // you can add whatever you want/need
],
“background”: {
“scripts”: [
// will scan file and listen for any pertinent events
“scripts.js”
],
“persistent”: false
}
}

Adding it to the manifest tells the chrome extension:

“Hey, listen. Let’s use this script right here to listen to what the customer wants. If they ask you to cover the images with these pictures of kittens then I want you running after them. If they pick up a family photo book I want you slapping the picture on top of anything they look at!”

The extension then reads the background script and listens for the event listeners listed within. What does this script.js look like? Well inside of the background scripts we can set up all of our action listeners. For this app we are going to need to set some rules to display our page action and some functionality to listen the the user clicks on the page action.


// scripts.js
// Remove the current rules
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
// Replace the current rules
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [/*curious? read more! */],
actions: [/* Go ahead, I know you want to*/]
}
])
})

We have the rule update functionality set up for now, as you can see we delete the current rules in bulk and then write over them on each page change because added rules are saved across restarts. Removing an individual rule is computationally expensive and can cause the data structures being created under the hood to duplicate. Wait! We are missing our rules, but exactly how do we set up our rules? I noticed that there always seemed to be two properties defined for each object inside the rule array parameter. The first property being “conditions”, this requires something called PageStateMatcher. This is the only supported condition based off the chrome.declarative api documentation. This condition allows us to set which web pages our extension is available in and which actions it is allowed to take based on the url, query, schemes, ports, css, and even if the page is bookmarked.

The second property that I have noticed is “actions” and this property usually has only one thing inside it. So far I have only seen “new chrome.declarative.ShowPageAction()” being used but there are also two other actions you can be utilized SetIcon, and RequestContentScript. This event object then displays the page action by the url bar. Let’s see what I mean in code:


// scripts.js
// Remove the current rules
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
// Replace the current rules
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
/*
Create a new event Object with PageStateMatcher that
matches page urls that follolw http and https schemes
*/
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
// hostEquals:
schemes: [ ‘http’, ‘https’]
}
})
],
actions: [
/*
displays the page action
*/
new chrome.declarativeContent.ShowPageAction()
]
}])
})

The declarativeContent API is now checking each rule and if a condition is met the corresponding actions would be executed. In this case our page action will be displayed. This isn’t enough… our page doesn’t do anything! Let’s set up a on click event listener for the page action. We want it to run code that we have inside of main.js that reads the document and changes the src to each image tag to one of an image of a kitten. To do this we would have to add a new event listener on the page action.

// code above this line// listen for when someone clicks the page action
chrome.pageAction.onClicked.addListener( function () {
// query the current tab on the current window
chrome.tabs.query( { active: true, currentWindow: true }, function ( tabs ) {
// exceute the main.js script on this tab
chrome.tabs.executeScripts(
tabs[0].id,
{ file: ‘main.js’ }
);
});
});

Luckily our declarativeContent API created a way for us to be able to do this. It allows for us to show our page action without the need to add it to the host permissions and without having to inject a content script that makes that functionality available. All we would have to do is add the code above to the current scripts.js code. It is it’s own separate thing so make sure to put it on a new line! Now when we click on the page action we run the code in main.js, which we still have to implement. Let’s go do that.

Running scripts based on events

We want the functionality inside of our main.js file to grab every image tag on our current page and make sure that the src attribute gets replaced when the move hovers into the area the image covers on the screen. We also want the extension to restore the original image when the mouse is not hovering over the image. We also want to be able to keep track of the original source so we can put it back when we move our mouse out the the images boundaries. I immediately think of event listeners when I think of the mouse, clicking, or hovering. So let’s go ahead and start this off.


// main.js
// we have access to the current tabs document variable because
// of activeTab and declarativeContent permissions.
let allImages = document.getElementsByTagName(‘img’);// we should now have an array of all the elements with the img tag. // this array will act as a queuelet oldImages = [];// iterate through each imgfor ( let i = 0; i) {
// attach an event listener for the mouse onto each image.
allImages[i].addEventListener( ‘mouseover’, function () { // grab the height and width of this current image let imageWidth = this.width;
let imageHeight = this.height;

// push the current source into the oldImages queue
oldImages.push(this.getAttribute(‘src’)); // set the new src for the image using placekitten.com this.setAttribute(‘src’, ‘http://placekitten.com/ ‘+ imageHeight + ‘/’ + imageWidth );
});
// add another listener for when the mouse leaves
allImages[i].addEventListener(‘mouseleave’, function () {
// get the old src up in queue and replace the current src
this.setAttribute(‘src’, oldimages.shift());
});
};

Now if you go to your chrome browser and search on google for images of dogs then activate your chrome extension, when you hover over images of dogs they turn to cats and back to their original picture when you go to another image. I decided to pivot last minute because I found this. I could not resist. This should complete the tutorial on “How to make a chrome extension”. I hope I helped demystify how to get started making a chrome extension and can’t wait to possibly use something you make in the future!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade