I made a Chrome extension this weekend because I found I was doing the same task over and over and wanted to automate it. Plus, I’m a nerd during a pandemic, so I spend my weird pent-up energy building things. I’ve made five Chrome extensions with that energy, yet I still find it hard to locate the docs to make them. Some things are outdated or deprecated. Some things are simply buried. I’m writing this as a bit of tutorial (1) in case it’s helpful to others and certainly (2) for myself the next time I want to build a Chrome extension.

Let’s get started.

Create the manifest

The very first step is creating a manifest.json file in a project folder. This serves a similar purpose to a package.json, only it provides the Chrome Web Store with critical information about the project, including the name, version, the required permissions, and so forth. Here’s an example:

{
 "manifest_version": 2,
 "name": "Sample Name",
 "version": "1.0.0",
 "description": "This is a sample description",
 "short_name": "Short Sample Name",
 "permissions": ["activeTab", "declarativeContent", "storage", "<all_urls>"],
 "content_scripts": [
   {
     "matches": ["<all_urls>"],
     "css": ["background.css"],
     "js": ["https://css-tricks.com/background.js"]
   }
 ],
 "browser_action": {
   "default_title": "Does a thing when you do a thing",
   "default_popup": "popup.html",
   "default_icon": {
     "16": "icons/icon16.png",
     "32": "icons/icon32.png"
   }
 }
}

You might notice a few things, like not all of that data is necessary. The names and descriptions can be anything you’d like.

The permissions depend on what the extension needs to do. We have ["activeTab", "declarativeContent", "storage", "<all_urls>"] in this example because this particular extension needs information about the active tab, needs to change the page content, needs to access localStorage, and needs to be active on all sites. If it only need it to be active on one site at a time, then there’s no need for that line and it can be removed altogether. 

A list of all of the permissions and what they mean can be found in Chrome’s extension docs.

"content_scripts": [
  {
    "matches": ["<all_urls>"],
    "css": ["background.css"],
    "js": ["https://css-tricks.com/background.js"]
  }
],

The content_scripts section sets the sites where the extension should be active. If you want a single site, like Twitter for example, you would say ["https://twitter.com/*"]. The CSS and JavaScript files are everything needed for extensions. For instance, my productive Twitter extension uses these files to override Twitter’s default appearance.

"browser_action": {
  "default_title": "Does a thing when you do a thing",
  "default_popup": "popup.html",
  "default_icon": {
    "16": "icons/icon16.png",
    "32": "icons/icon32.png"
  }
}

There are things in browser_action that are also optional. For example, if the extension doesn’t need a popup for its functionality, then both the default_title and default_popup can be removed. In that case, all that’s needed the icon for the extension. If the extension only works on some sites, then Chrome will grey out the icon when it’s inactive.

Debugging

Once the manifest, CSS and JavaScript files are ready, head over to chrome://extensions/from the browser’s address bar and enable developer mode. That activates the “Load unpacked” button to add the extension files. It’s also possible to toggle whether or not the developer version of the extension is active.

I would highly recommend starting a GitHub repository to version control the files at this point. It’s a good way to save the work.

The extension needs to be reloaded from this interface when it is updated. A little refresh icon will display on the screen. Also, if the extension has any errors during development, it will show an error button with a stack trace and more info here as well.

Popup functionality

If the extension need to make use of a popup, it’s thankfully fairly straightforward. After designating the name of the file with browser_action in the manifest file, a page can be built with whatever HTML and CSS… including a popup!

Now, we’ll probably want to add some functionality to a popup. That make take some JavaScript, so make sure the JavaScript file is designated in the manifest file and is linked up in your popup file as well, like this: <script src="https://css-tricks.com/background.js"></script>

In that file, start to creating functionality and we’ll have access to the popup DOM like this:

document.addEventListener("DOMContentLoaded", () => {
 var button = document.getElementById("submit")

 button.addEventListener("click", (e) => {
   console.log(e)
 })
})

If we create a button in the popup.html file, assign it an ID called submit, and then return a console log, you might notice that nothing is actually logged in the console. That’s because we’re in a different context, meaning we’ll need to right-click on the popup and open up DevTools.

Showing the "Inspect" option to open DevTools after right-clicking on an element on the page.

We now have access to logging and debugging! Keep in mind, though, that if anything is set in localStorage, then it will only exist in the extension’s localStorage; not the user’s browser localStorage. (This bit me the first time I tried!)

Running scripts outside the extension

This is all fine and good, but say we want to run a script that has access to information on the current tab? Here’s a couple of ways we would do this. I would typically call a separate function from inside the DOMContentLoaded event listener:

Example 1: Activate a file

function exampleFunction() {
 chrome.tabs.executeScript(() => {
   chrome.tabs.executeScript({ file: "content.js" })
 })
}

Example 2: Just execute a bit of code

This way is great if there’s only a small bit of code to run. However, it quickly gets tough to work with since it requires passing everything as a string or template literal.

function exampleFunction() {
 chrome.tabs.executeScript({
   code: `console.log(‘hi there’)`
  })
}

Example 3: Activate a file and pass a parameter

Remember, the extension and tab are operating in different contexts. That makes passing parameters between them a not-so-trivial task. What we’ll do here is nest the first two examples to pass a bit of code into the second file. I will store everything I need in a single option, but we’ll have to stringify the object for that to work properly.

function exampleFunction(options) {
 chrome.tabs.executeScript(
   { code: "var options = " + JSON.stringify(options) },
   function() {
     chrome.tabs.executeScript({ file: "content.js" })
   }
 )
}

Icons

Even though the manifest file only defines two icons, we need two more to officially submit the extension to the Chrome Web Store: one that’s 128px square, and one that I call icon128_proper.png, which is also 128px, but has a little padding inside it between the edge of the image and the icon.

Keep in mind that whatever icon is used needs to look good both in light mode and dark mode for the browser. I usually find my icons on the Noun Project.

Submitting to the Chrome Web Store

Now we get to head over to the Chrome Web Store developer console to submit the extension! Click the “New Item” button, the drag and drop the zipped project file into the uploader.

From there, Chrome will ask a few questions about the extension, request information about the permissions requested in the extension and why they’re needed. Fair warning: requesting “activeTab” or “tabs” permissions will require a longer review to make sure the code isn’t doing anything abusive.

That’s it! This should get you all set up and on your way to building a Chrome browser extension!





Source link

Write A Comment