Building a simple Google Chrome extension
Tuesday, 26 January 2010 08:41
I have a web app running on my home server to keep track of my bookmarks—it's a little like Delicious, but simpler and with some personal customisations. Currently I save bookmarks to this app via a Javascript bookmarklet: clicking it gets the current page's title and url (and also any selected text, to use as a summary) and sends it to a popup form; submitting that form then saves the bookmark data to the server.
Although this system works well enough, it looks a bit untidy and takes up space in the bookmarks bar. With the advent of Extensions for Chrome, I thought I'd have a go at writing an extension to nicely integrate my custom page bookmarking button into the Chrome browser.

It's clear from the start that Chrome's extension structure is a lot simpler than that of Firefox extensions. Chrome extensions are just a collection of plain HTML and JavaScript files—no odd folder hierarchies or XUL to deal with here. Of course, there are several advantages to Mozilla's approach (ease of internationalisation, UI consistency), but I can't help feeling that building Chrome extensions will be much more accessible to amateur developers; I'm betting that this is exactly what Google was aiming for.
So let's get stuck in! First create a new folder for your extension code—it doesn't matter where for now. My basic Chrome extension consists of just a few files:
manifest.json
This is the glue that holds our extension together. It contains the basic meta data about the extension (title, description etc), as well as acting as a pointer to the various files that contain the extension's user interface and JavaScript code. It also defines permissions that specify which browser components and external URLs the extension is allowed to access. The manifest for our extension looks like this:
{
"name": "Bookmark",
"description": "Adds the current page to my bookmarking system.",
"version": "1.0",
"background_page": "background.html",
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
],
"browser_action": {
"default_title": "Bookmark This Page",
"default_icon": "icon.png",
"popup": "popup.html"
}
}
The background_page property points to an HTML page which contains the logic code for the extension. This HTML is never displayed, it just interacts with the browser and page via JavaScript. The browser_action section defines a button with an icon, which the user will click to open the bookmarking dialog, and the popup property which points to the HTML file containing the dialog form.
popup.html
This file contains a basic HTML form with title, url, summary and tag fields (so that we can edit and tag our page bookmark before saving it), and some JavaScript code to do the population and saving of the fields. You can download the complete source here, but for now the important part is the script:
// This callback function is called when the content script has been
// injected and returned its results
function onPageInfo(o)
{
document.getElementById("title").value = o.title;
document.getElementById("url").value = o.url;
document.getElementById("summary").innerText = o.summary;
}
// POST the data to the server using XMLHttpRequest
function addBookmark(f)
{
var req = new XMLHttpRequest();
req.open("POST", "http://mywebappurl/do_add_bookmark/", true);
var params = "title=" + document.getElementById("title").value +
"&url=" + document.getElementById("url").value +
"&summary=" + document.getElementById("summary").value +
"&tags=" + document.getElementById("tags").value;
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", params.length);
req.setRequestHeader("Connection", "close");
req.send(params);
req.onreadystatechange = function()
{
// If the request completed, close the extension popup
if (req.readyState == 4)
if (req.status == 200) window.close();
};
return false;
}
// Call the getPageInfo function in the background page, passing in
// our onPageInfo function as the callback
window.onload = function()
{
var bg = chrome.extension.getBackgroundPage();
bg.getPageInfo(onPageInfo);
}
This may look a little confusing at first, but it will hopefully make more sense when you see the other code.
background.html
Think of this file as the negotiator between the popup dialog and the content/DOM of the currently loaded web page. Though it's an HTML file, it only needs contain a single script tag (as shown below); it will never be displayed anywhere. getPageInfo is the function we called when our popup loaded, and its parameter is the callback function which sets the values of the form fields in popup.html.
<script>
// Array to hold callback functions
var callbacks = [];
// This function is called onload in the popup code
function getPageInfo(callback)
{
// Add the callback to the queue
callbacks.push(callback);
// Injects the content script into the current page
chrome.tabs.executeScript(null, { file: "content_script.js" });
};
// Perform the callback when a request is received from the content script
chrome.extension.onRequest.addListener(function(request)
{
// Get the first callback in the callbacks array
// and remove it from the array
var callback = callbacks.shift();
// Call the callback function
callback(request);
});
</script>
When getPageInfo is called, it pushes the callback function onto a queue and then injects the content script (below) into the code of the current web page.
content_script.js
The content script itself is pretty simple: it just gets the title, url and any selected text from the current page and fires them back the the background page.
// Object to hold information about the current page
var pageInfo = {
"title": document.title,
"url": window.location.href,
"summary": window.getSelection().toString()
};
// Send the information back to the extension
chrome.extension.sendRequest(pageInfo);
The background page listener then gets the callback function from the queue (which, if you remember, is the onPageInfo function from the popup page) and calls it, passing in the information about the page so that it can populate the form field values.
Testing and installing the extension is much easier than in Firefox, too. All you need to do is click the Chrome "spanner" icon at top right and select Extensions. Once you're on the Extensions tab, click Developer Mode, browse to your extension's folder and select it. You'll see the icon appear in your browser toolbar; click it while viewing any normal web page and you should see a popup like the one in the screen shot at the beginning of the article, populated with the data from the current page.
You can download all the source code here and modify it to suit your own purposes, or just use it to learn from.
That's it! I'll explain more about Chrome extensions in future posts, but in the meantime, the Google extension documentation is comprehensive and very useful to learn from. I also picked up a lot of good information from this thread on the Chromium Extensions Google Group.
Comments
Matthew 25/05/2010 06:17
hi, I'm trying to build an extension, using a content script to modify a <div> and add a link, to a php file, which fills out a form and automatically submits. [for avid Craigslisters who want to send an ad to their inbox with one click]. So, I want to use a http:// ... com/craigslist.php?addr=myemail@myhost.com in the url to determine what to put in the email values, which have been set by "localStorage.MAIL = textbox.value", defined in popup.html, or options.html. I can't figure out how to do this, but I hear you have to use "messaging" or "request," to background.html. If you can explain how to do this, I'd appreciated it! :)
vipul 19/08/2010 11:24
Hi
Was looking for similar implementation. but I was wondering if using chrome extension one can make call to local server(w/o using gears api) from extesion. Would like to know how did u do this.
vipul PS:Download link is not working.
Dan M 26/08/2010 04:37
Hey Mark, Really great example thanks. Would you be able to have a look at the download link as it's not working at the moment and it'd be great to see the whole thing Thanks Dan
Nick Yeoman 01/09/2010 09:14
your download link isn't working
Mark Bell 20/10/2010 08:14
Apologies everyone, the download link is now working again.
David 09/04/2011 11:30
Excellent article - simplest and best I've found. Thanks to you, I got my extension working (at last! :) Great job.
Jason 22/05/2011 08:27
Thank you very much for this outstanding article and example!
Prabhakar 29/05/2011 07:13
How to change the color of the web page, when it gets loaded(onLoad()) ?
ukungzulfah 19/07/2011 07:39
I'm just learning and I want to ask, how the implementation sendRequest if the server is outside. eg: (popup.html communication with http://www.blabla.com/info.php) with ajax.
Arvind 11/09/2011 01:36
I am trying to obtain the content of an iframe- the js code to obtain this content is injected programatically into the web page via browser action (ie user clicking on icon of extension)-- the js code (which in a regular html page works) gives error "Uncaught TypeError: Cannot read property 'document' of undefined" when used as part of js file in google chrome extension... I cant figure out why its not working... I would be grateful if you could help me with this...
The relevant js code is given below-
var myIFrame = document.getElementById('iframe1'); var content = myIFrame.contentWindow.document.body.innerHTML; // Getting it's content into a variable alert('content: ' + content); // here it is
Thanks, Arvind.
varma 20/10/2011 06:51
Great explanation! It really helped appreciate it
varma 20/10/2011 06:53
Between the question below to post the comments sucks. If you want to prevent spam there other ways I suppose. why do I care about octopus. To thank you I had to find out the answer. Anyways not my problem.
Sameer 19/11/2011 11:42
I have a quetions that why do we need to maintain queue of callbacks in background.html. I mean why can't we just keep a single copy of callback(rather than array) and as soon as request comes in the previous copy will be overwritten. As copy of popup always be one. Do I missing something? I just want to confirm.
Jake D. Snake 18/01/2012 07:12
I just want to ask... can this extension load all his bookmarks to the bookmarks bar too??? or does this just saves the bookmarks in his server??? thanks...
Holly Cams 09/05/2012 05:16
You don't need background nor content script. Just use chrome.extensions.tabs API to get page URL and etc