Building a Simple Chrome Extension

I decided to make my first experimental Chrome Extension. My colleague came up with a really simple idea to implement, so I decided to give it a try.

The Functional Requirement

Create a Chrome Extension that will output a small colored square in the top left corner of a page, alerting you of which type of domain (i.e. .dev, .stage) you're on. These domains and colors will be managed on an Options Page.

Options Page

The environments and their corresponding color should be managed on a Options Page, allowing you to add / remove any number of entries.

Active Tab

The small square should only appear on domains that match entries the user has added on the Options Page.

Notable Settings

Background Scripts

Extensions are event based programs used to modify or enhance the Chrome browsing experience. Events are browser triggers, such as navigating to a new page, removing a bookmark, or closing a tab. Extensions monitor these events in their background script, then react with specified instructions.

We'll be using background scripts to add an event listener to the onInstalled event.

This will allow us to run code when the extension is installed. We'll use this event to add some default entries for the Options Page.

{"background":{"scripts":["background.js"],"persistent":false}}

Why is persistent marked as false? As the documentation states:

The only occasion to keep a background script persistently active is if the extension uses chrome.webRequest API to block or modify network requests. The webRequest API is incompatible with non-persistent background pages.

Content Scripts

Content scripts are files that run in the context of web pages. By using the standard Document Object Model (DOM), they are able to read details of the web pages the browser visits, make changes to them and pass information to their parent extension.

Essentially, any script you would like to actually run on a given page, needs to leverage this api. In our example, we'll be injecting a colored square in the top left corner of the active tab.

Go ahead and review the options folder before we continue. It's a pretty standard Vue application.

Let's review some of the notable Vue code. Options.vue is where most of the magic happens with leveraging the chrome api.

// options/Options.vue{data(){return{/**
* empty array to be used to store
* the chrome storage result
*/config:[],};},mounted(){/**
* once the component mounts
* lets call the storage api
* and request our `config` key
*
* on our callback, lets call a method
* to set our internal state
*/chrome.storage.sync.get(['config'],this.setConfig);},methods:{setConfig(storage){/**
* set our internal state
* with the result from the
* chrome api call
*/this.config=storage.config;},},}

In the code above, we are doing the following:

setting internal state for a key called config, and assigning it to an empty array

on the mounted() method, we are requesting the key config from the storage api

on the callback function, we call a method called this.setConfig

setConfig() assigns our internal state to what is returned from the chrome api

We then have two methods for altering the chrome storage state:

{deleteEntry(index){/**
* remove the entry at a specific index
* from our internal state
*/this.config.splice(index,1);/**
* update the chrome storage api
* with the new state
*/chrome.storage.sync.set({config:this.config,},null);},addEntry(entry){/**
* add an entry to our internal state
*/this.config.push(entry);/**
* update the chrome storage api
* with the new state
*/chrome.storage.sync.set({config:this.config,},null);},}

After implementing these methods, the final Options Page looks like this:

I know, it's nothing fancy… but that's not the point. Get out there and have some fun! You'll notice I added a edu domain, go ahead and add that now if you would like.

Content Script

Now that we have an Options Page with a way to add / delete entries, let's now implement the small square that will appear in the top left corner of valid domains.

To do this, we need to use the content script we discussed before. Let's go ahead and open up the content/content.js file.

// content/content.js/**
* lets first request the `config` key from
* the chrome api storage
*/chrome.storage.sync.get(['config'],({config})=>{/**
* lets see if the `window.location.origin`
* matches any entry from our
* options page
*/letmatch=config.find((entry)=>{letregex=RegExp(`${entry.domain}\/?$`);returnregex.test(window.location.origin);});/**
* if no match, don't do anything
*/if(!match)return;/**
* lets create the style attribute
* by building up an object
* then using join to combine it
*/letnode=document.createElement('div');letnodeStyleProperties={'background-color':match.color,height:'25px',left:'5px',opacity:0.5,'pointer-events':'none',position:'fixed',top:'5px',width:'25px','z-index':'999999',};letnodeStyle=Object.entries(nodeStyleProperties).map(([key,value])=>{return`${key}: ${value}`;}).join('; ');/**
* apply the style to the node
* and a class flag (doesn't do anything)
*/node.setAttribute('style',nodeStyle);node.setAttribute('class','chrome-extension-environment-flag');/**
* append the node to the document
*/document.body.appendChild(node);});

Conclusion

Now, when I go to an edu domain, I see the following in the top left corner:

I hope this tutorial at least got you interested in Chrome Extensions. We only scratched the surface. Feel free to use any of the code in my repo for any purpose.