Creating a Chrome Extension with VS Code

Creating a Chrome Extension with VS Code

Hooray, I have built my first Chrome extension! Why and how? Annoyed by the automated translation of Microsoft Docs, I decided to do something about it. You can read all the details in this post and lean how easy it is to get started on your first Chrome extension.

Background Story

As you might have noticed by now, all the Microsoft documentation has been shifted to docs.microsoft.com. And to be fair; I like it! The docs are well written, indexed so I can find everything very fast and like everything at Microsoft these days: the docs are open-source. This means you can even contribute yourself.

There is one thing I don’t like that much, and that is the automated translations. If you’re not from an English native country like me, Docs tries to serve you a localized version of the page you want to reach. If there isn’t a human-translated one, they will translate it with the power of Azure and show you that.

While this seems like a good idea in theory, in practice it doesn’t work that great. Technical content is very hard to translate in an automated matter, mostly because there are so much technical terms that you just want to keep English and translates sometimes quite hilarious. Because of this, reading becomes harder and you get distracted of what is trying to be explained.

The worst thing is: you can’t override this behavior from Docs. Per page you have a toggle in the top right which lets you show the original version, but just for that page. The is no way to persist it and just say; hey Microsoft! I always want the English content!

I got tired of toggling the same thing over and over again and decided to look into developing a Chrome Extension for this. I’m a developer, how hard can it be?!

Actually, it was quite easy!

First Contact

So, there I was. Laptop open, ready to go, but no clue how to start. I did notice the “developer mode” toggle in the extensions page before and decide to have a play with it. It gave me a couple of extra buttons, but no clue on how to get started, you can see this depicted in the image underneath. I turned to my best friend Google and simply searched for “developing Chrome extension”. I quickly got to this page: https://developer.chrome.com/extensions/getstarted

Extension overview page
Extension overview page

This “Getting started tutorial” teaches you the basics by building a simple sample extension. As I suspected, Chrome extensions are built in Javascript, HTML and some JSON, no surprise there. From this page, I gathered that your extension starts by creating a manifest.json. Since it’s all Javascript and alike, the tooling does not really matter. Being the Microsoft-minded guy that I am I picked up my favorite tool from the shed: Visual Studio Code.

Manifest.json

The manifest.json file is basically the entry of your extension. Here you declare what files your extension consists of, what permissions it needs, add some nice icons and specify other metadata. The manifest.json is, as far as I know at this point, the only file that has to have a specific filename. All other files are referenced from there and can be in folders or subfolders.

Below you can see the manifest.json file that I ended up with.

To be fair, I didn’t deep-dive into all of the specifics of building a Chrome extension, but I think the keys in the above file is pretty much the least you need to have in there.

Keys

There are some obvious keys in there. You will have to give your extension a name, description and version. Also, you have to specify the manifest_version. At this time, simply set it to 2. This specifies which version of the manifest you are using, so the Chrome extension parsing knows what keys to look for. At the time of writing, though, version 1 is no longer supported and there is no version 3, so 2 it is!

Then there is a key called background. I will explain this in more detail in the next section about extension types. Here you just need to reference the script(s) that can run in the background. Basically, you reference the Javascript file that does the work you want it to do.

With the options_page key, you specify the HTML page that is opened whenever a user navigates to the settings of your extension. All extensions will have a button in the Chrome toolbar, if you press it you can go to the settings, here is where you specify what that settings page is.

The HTML page can have anything you like. I downloaded Bootstrap and jQuery and hooked it up here to make it look nice and work easily for me. You can probably load it from a CDN to keep your extension size smaller, but I liked the idea of keeping it self-contained. Within the HTML page, you can load your own Javascript and do your thing with that. I quickly found out that to persist settings and read them in another place, you should simply use the browsers local storage.

The page_action key will define what is shown whenever a user hits a certain page. This is also something that is explained in more detail in the next section.

Lastly, there is a couple of icon keys in there. Just make sure you have a nice icon and supply it in a number of different formats.

To read up in detail on all the different keys in here, refer to the Google documentation at https://developer.chrome.com/apps/manifest.

Different Extension Types

As far as I can tell there are roughly two types of extensions you can build: either user triggered or background.

The thing I wanted to do is take the URL the user might be navigating to, check if it contains the Microsoft Docs URL and then swap out the localization code if needed. Since this is something that should happen automatically, this would be a background plugin.

You can also choose to build an extension that acts whenever the user interacts with it. Regardless of if an extension is a background extension or not, it will always have an icon in the toolbar. From that icon, you can spawn a pop-up, which is just an HTML page where you can have your user interact and from there trigger any logic that you want. The difference here is if you implement it as a page action or a browser action.

With both you can do the exact same thing, the difference is when your toolbar icon is activated. Your popup will only show whenever the button is activated. As the name already implies: a page action only activated whenever the user lands on a certain page, and a browser action is available to every page. The guideline Google prescribes for this is:

  • Do use page actions for features that make sense for only a few pages.
  • Don’t use page actions for features that make sense for most pages. Use browser actions instead.
  • Don’t constantly animate your icon. That’s just annoying.

Page Action

In my case, I went for a page action. Since the extension is very specific for just one website, or at least one base address, I decided to go for page action. You don’t even have to include a page action if you don’t want to, but it will look a bit nicer, in my opinion, to let the user know your plugin is active on this page. When you do not include browser action or page action, your extension icon – which is always there – will always look disabled. With a browser action, it will always look enabled, since it is relevant to the browser and all pages. If you use the page action, it will only become active on the page(s) that you define. To define on which page your icon should be active, you use this piece of code in your background JS file:

Lighting up my toolbar icon at the Microsoft Docs page

This code adds a rule and checks if the host is equal to, in this case, docs.microsoft.com. Whenever it is, the icon lights up and when the user clicks it, your popup will come up. If the icon is disabled and the user clicks it, it will show a default context menu.

By defining the default_popup, you set a page that will pop-up whenever your extension icon is active and the user clicks on it. In my extension, I have given it an HTML page that allows you to configure the plugin. What it really does is the same thing as my full-blown settings page. As I said; it doesn’t add that much value, it’s more like a minor UX improvement. As I mentioned earlier, you just store your settings in the local storage of your plugin, so you can access that from your popup, the settings page or the scripts that are associated to your extension.

Chrome Events

There are a whole number of events that you can hook into and act upon to implement the functionality you desire with your extension. Note that you will have to look into

Debugging

The debugging experience was quite good! Because it all runs in Chrome and it’s all web languages, you can , debug everything with the Chrome Developer Tools that are already available to you in the browser. There is, however, a difference between unpacked and packed debugging. Ask me how I know…

Unpacked

Unpacked is the name they gave to just a folder which contains all your files. You can simply point to that folder from the extensions screen in developer mode, and it will load the extension like it would normally. The extra thing you get is that you can debug through it.

Depending on what kind of extension you are building you can just go into the developer tools as you normally would, set breakpoints and trigger them. However, if you’re building a background extension like me, you will have to open the developer console from the extensions page. If you go there, you will notice that the words “background page” are blue, indicating that it is a link. When you click on it, you will get the same developer console as you would normally open yourself on a page.

Extension details on the chrome://extensions page
Extension details on the chrome://extensions page

Packed

A packed extension is actually the release version. The important thing you should know here is that when using an unpacked extension, you seem to have less limitations. So, what happened was; I thought I was done, packed up my extension (note: it with the pack button, but simply zip it) and took it to the extension store. After it was published I decided to uninstall my development version and download it from the store to see if the behavior was the same. As it turned out, it wasn’t.

At first I was a bit confused on how I should then test my extension when the unpacked version worked properly. After a little search engine action, I found out that you can bypass the store by packing your extension with the designated button in the extension screen and then drag the packed extension back into your Chrome instance. That way, your extension is installed the same way as it would through the store. There is a field there for a signing key, as far as I can tell it’s not important since it’s just used for local debugging. One might expect that this is also the package that you need for distribution, but it is not as we will learn a little bit later on.

Release and Distribution

After little more than an hour, I had the first version of my extension ready, pretty fast, right? Thinking to myself I couldn’t be the only one being annoyed by the Microsoft Docs translation fairy, I decided to put this in the extension store. So, the next step would be to find out how to publish it there.

Developer Account

Although I already have an Android developer account, this is apparently not a global Google developer account. I had to crest a separate account for the Chrome extension. Don’t worry though, the process is pretty simple and can be completed within a matter of minutes. If you’re already a Google user, that is.

All I needed to do was click on the developer console in the options and enter some details. The funny thing is, is that I had to pay a small amount of 5 dollars (once) to be able to publish to the store. For your 5 dollars you can even put 20 extensions in the store! Probably to prevent people from creating all kinds of minor extensions and take them to the store. Oddly enough, there was no further review of my code. At least, not that I’m aware of.

Anyway, after going through these hoops I was taken to the developer dashboard, which looks horrific by the way, and I could start creating my extension entry. To create the entry, you have to enter some simple data like a description and some images. Then to add your actual extension, you simply bump up the version number in your manifest.json file, zip the folder and upload that to the developer portal.

By the way, if you look closely at the old developer portal, there is an alert that tells you the new developer portal is in preview and you could try it. You should! It’s much, much better and if you, by any chance, know the Android developer portal, it looks pretty much the same.

So, as I mentioned earlier, my extension showed some weird behavior in production. It would work for a second or two, but then it stopped! At first I couldn’t figure out what was going on, until I noticed this key in my manifest file: 

The culprit

This seemed odd, and I quickly took it out to see what was happening, According to this page, you should not persist your background scripts, unless you are using the chrome.webRequest object. And guess what I was using… So after that little quirk, everything was up and running!

Summary

Creating a Chrome extension isn’t that hard, but can solve some issues that you might have with certain web applications or really add value. For instance, I have an extension for ages that lets me pick a color from the page that I am on. And now, for this hassle I encountered myself, I have created a small piece of code, that I learned from, and I won’t be bothered with poor Dutch translations any longer.

If you want to use the extension, you can get it from here: https://chrome.google.com/webstore/detail/microsoft-docs-langyfier/gkneebafjlcpcgahjdlmdoejhjnfleha. If you like it, please rate and/or share it!

I hope you got a great start of this write-up and please let me know if you have created any cool extensions of your own, or if you are using mine. Everything described here is open-source on GitHub: https://github.com/jfversluis/Microsoft-Docs-Langyfier and I will gladly look at any issues or accept any improvements through pull requests.