/ Engineering

How to write your first Chrome Extension

Chrome extensions are surprisingly easy to write once you understand how they work. They are pieces of HTML and Javascript which are glued together with a JSON file called manifest.json. This means you can use your favourite Javascript frameworks (e.g. Angular, Backbone, jQuery) when building them.

If you’re new to Chrome extensions and want to write one, here’s a post that will give you an introductory understanding on how they work. This post will touch on each of the building blocks, how they communicate with each other and the typical structure of a Chrome extension.

Building Blocks

There are generally two types of files in a Chrome extension: files that exist at the Chrome application level (e.g. Background and Popup) and files that are injected onto the DOM of each website (e.g. Content Scripts and Injected Scripts). These files are brought together using manifest.json, which identifies the role of each file so Chrome knows how to pull them together.

At any one time, only one copy of Background and Popup is running on Chrome. In contrast, if you have several tabs opened, one copy of Content Scripts and Injected Scripts could be running for each tab. I say ‘could’ because you can specify urls where Content and Injected Scripts are injected. The diagram above illustrates this.

Below are some bullets on how each of these pieces work:

Manifest.json

  • JSON file that brings together the backgrounds, popups, content scripts and injected scripts by specifying where they are in the file structure
  • Configures the extension (e.g. permissions)
  • Contains general extension related information (e.g. name, version)

Background:

  • Javascript file(s) that always run in background of Chrome (N.B. in older versions of Chrome, background used to be a html file with embedded script tags)
  • No view
  • Has access to Chrome application level commands
  • Full access to all permitted chrome.* APIs
  • HTML and Javascript that appears when you click the Chrome extension icon (see diagram below)
  • Exists on same application level as background
  • Only active when Chrome extension button is clicked
  • Full access to all permitted chrome.* APIs
    chrome-extension-popup

Content Scripts:

  • Partial access to some of the chrome APIs (e.g. able to listen to messages from background)
  • Full access to the page’s DOM
    No access to any of the window objects (e.g. global variables), including frames. This is mostly for security reasons.
  • Content scripts run in a scope between the extension and the page. The global window object of a Content Script is distinct from the page/extension’s global namespace.

Injected Scripts:

  • Like Content Scripts, except no access to any of the chrome.* APIs.
  • Injected scripts behave as if they were included by the page itself, and are not connected to the extension in any way.

Communication

Communication between pieces is straightforward once you understand the overall structure.

Pieces that exist on the application level have direct access to each other. For instance, Popup files can access Background by using chrome.extension.getBackgroundPage(). This is similar to how Backbone Views have access to their Models or Collections.

Content Scripts, which exist on the individual page DOMs talk to Background and Popup using messages. Specifically, they can use chrome.tabs.sendMessage and chrome.runtime.onMessage.addListener to send messages to each other. This is similar to the Backbone eventing system.

Injected Scripts are different from Content Scripts in that they cannot listen or message the other pieces in the Chrome extension.

Structure

To organise your Chrome extension, it might be helpful to separate the different pieces into folders that represent their role. You can also use bower to pull in dependencies like you would in any other project. Here’s how I structured my Chrome application:

|___manifest.json
|___bg
|   |___background.js
|___content-scripts
|   |___player.js
|   |___recorder.js
|___injected-scripts
|___popups
|   |___popup.html
|___styles
|   |___styles.css
|___bower.json
|___bower_components
|___img
|   |___icon.png

The Manifest.json file then brings these pieces together (see example below). An important thing to note is that files are compiled in the order that they are specified, so dependencies should be listed before the actual script files. In the example below, jQuery is loaded before the content scripts recorder.js and player.js.

{
  # default for the latest version of Chrome extensions
  "manifest_version": 2,
  # extension related general info
  "name": "MyExtension",
  "description": "MyExtension",
  "version": "1.0.0",
  # sets path to popup files
  "browser_action": {
    "default_icon": "img/icon.png",
    "default_popup": "popups/popup.html"
  },
  # sets path to content scripts and when they are injected onto the page
  "content_scripts": [{
    "matches": ["http://*/*","https://*/*"],
    "css":["styles/styles.css"],
    "js": [
      "bower_components/jquery/dist/jquery.min.js",
      "content-scripts/recorder.js",
      "content-scripts/player.js",
    ]
  }],
  # sets path to background scripts
  "background": {
    "scripts": [
      "bower_components/jquery/dist/jquery.min.js",
      "bg/background.js"
    ]
  },
  # sets permissions that extension has
  "permissions": [
    "<all_urls>",
    "tabs",
    "storage",
    "unlimitedStorage"
  ]
}

And that’s it! Hope this gives you an idea on how to write your first Chrome extension!