Chrome's extension system is far from finished, however I decided to give what they've got a trial run. This tutorial will provide a step-by-step guide on how to build a simple "Tweet This" extension for Google Chrome.
Chrome extensions are written in Javascript - with a splash of HTML and JSON thrown in. At the moment, the documentation is a bit limited, however they've provided a few example extensions that really help to understand what's going on. In order to run extensions, you'll have to first install the developer channel version of Chrome.
Below is a screenshot of what we'll be building today. It's simply a button that lives in Chrome's toolbar. When it's clicked, it will use bit.ly's shortening API to create a short url for the current page and take the user to Twitter's home page with the status box populated with the page's title and the shortened url.
The first thing you'll need is a place to store the extension files. For the rest of this tutorial, I'll assume they're placed in the folder, "C:\TweetThis\". Chrome uses a manifest JSON file for loading extensions. Let's start by getting a button to show up on the toolbar. Create a file, called manifest.json, in the folder above.
"name": "SOTC Tweet This for Chrome",
"version": "1.0",
"description": "Adds a Tweet This button on Chrome's Toolbar.",
"toolstrips" : [
"SOTC_tweet_this_toolstrip.html"
]
}
We'll be adding more to this as the tutorial progresses, but this is the basics. name, version, and description are pretty self explanatory. toolstrips holds an array of html documents that describe each button Chrome should add to the toolbar. Since we only want the one button, our array only holds one element. Of course, we now need to populate the html file referenced here.
Add a new file, SOTC_tweet_this_toolstrip.html, to C:\TweetThis to hold the contents of the button. Here's the contents of my Tweet This button. You can, of course, change this to fit any look you like.
<body>
<div class="toolstrip-button">
<img src="SOTC_tweet_this_icon.png" />
<span>Tweet This!</span>
</div>
</body>
</html>
As you can see, this is pretty straight forward HTML. All we have is a simple div to hold our button elements, an image, and a label. You'll have to save the image to C:\TweetThis with all of the other files.
You can now install this extension and see a button on the toolbar. Obviously, it won't do anything yet, but it will be visible. To load your extension, you'll need to modify the command arguments used when launching Chrome. Simply find or create a shortcut to Chrome and provide these arguments.
Once these are added, you'll have to restart Chrome to load the extension.
Next up, let's add some script to this extension. The first thing we need to do is get the URL of the currently opened tab. Unfortunately, this is a lot harder than it should be. Javascript, by default, will execute in the scope of the extension's page. This means, when we add script to handle the button click, it will not have access to the loaded web page. Google added something called content scripts, which can be executed in the scope of the loaded page. You then need to use messages to communicate between the content script and the extension. First we need to tell Chrome about the content script by adding to the manifest file.
"content_scripts": [
{
"js": [
"SOTC_tweet_this_content_script.js"
],
"matches": [
"http://*/*"
]
}
],
"name": "SOTC Tweet This for Chrome",
"version": "1.0",
"description": "Adds a Tweet This button on Chrome's Toolbar.",
"toolstrips" : [
"SOTC_tweet_this_toolstrip.html"
]
}
What we're providing is an array of content scripts. We only need one, so our array only contains one element. The first argument, js, should point to the script you'd like to run. The second argument, matches, is used to limit the script to specific pages. Since you can tweet about any page, we opened it up to everything.
Add a new file to the extension folder called SOTC_tweet_this_content_script.js to hold the content script.
window.addEventListener("focus", getUrl);
function getUrl() {
if(window == top) {
chrome.extension.connect().postMessage(
{
"url" : window.location.href,
"title" : window.document.title
});
}
}
This code will be executed in the same scope as any loaded page. All we're using it for is to get the URL and the title of the loaded page. Whenever a new window is loaded, we immediately call getUrl(), which checks to see if the window is the top-most window, and if it is, posts a message which will be caught by our extension code. We need to check for top-most since this code gets called for iFrames as well. We then hook the focus event so we can update the URL and title when the user switches between tabs.
All right, now we're starting to get to the meat of the tutorial. Let's update the button's HTML to add our extension script file and watch for the click event.
<head>
<script src="SOTC_tweet_this_script.js"></script>
</head>
<body>
<div class="toolstrip-button" onclick="tweet()">
<img src="SOTC_tweet_this_icon.png" />
<span>Tweet This!</span>
</div>
</body>
</html>
Nothing much changed here. I added a script tag that points to a script file that holds the logic for our extension. I also hooked the div's onclick event to the tweet function, which we'll be writing in a moment.
Create a new file called SOTC_tweet_this_script.js and put it in the extension folder with all of our other files. Inside this file, we'll start by simply creating some variables to hold the information we're going to need.
var title;
var username = "[your bit.ly username]";
var apiKey = "[your bit.ly api key]";
var tweetUrl = "http://twitter.com?status=";
var shortUrl;
Most of these are self explanatory. You'll have to sign up for a bit.ly account in order to use their shortening API. The process of signing up is quick and painless. Now let's add the listener for our content script's messages.
var title;
var username = "[your bit.ly username]";
var apiKey = "[your bit.ly api key]";
var tweetUrl = "http://twitter.com?status=";
var shortUrl;
chrome.self.onConnect.addListener(function(port) {
port.onMessage.addListener(function(data) {
longUrl = data.url;
title = data.title;
});
});
The syntax here is a bit verbose for its purpose, but there's not much we can do about it. Basically, whenever postMessage is called in our content script, this function will be called. The url and title are passed through the message and our variables are updated.
Now we need the most important function - tweet(). This is what gets called whenever the button is pressed.
var title;
var username = "[your bit.ly username]";
var apiKey = "[your bit.ly api key]";
var tweetUrl = "http://twitter.com?status=";
var shortUrl;
chrome.self.onConnect.addListener(function(port) {
port.onMessage.addListener(function(data) {
longUrl = data.url;
title = data.title;
});
});
function tweet() {
var req = new XMLHttpRequest();
req.open(
"GET",
"http://api.bit.ly/shorten?version=2.0.1&longUrl=" + longUrl +
"&login=" + username +
"&apiKey=" + apiKey,
true);
req.onload = function() {
var response = eval('(' + req.responseText + ')');
if(response && response.results) {
shortUrl = response.results[longUrl].shortUrl;
}
else {
shortUrl = longUrl;
}
window.open(tweetUrl + title + ' ' + shortUrl);
};
req.send(null);
}
The first thing we do is create an XMLHttpRequest object to handled the bit.ly API call. Chrome extensions don't have the cross-domain limitation that ordinary Javascript does, so we're allowed to make requests to any page we want.
Bit.ly responses come back as JSON, so when the request is complete we need to take the response and call eval on it to turn it into a Javascript object. I do some really simple error checking to make sure everything came back correctly. If something isn't right, I just set the short URL to the long one since that's better than nothing. If everything went well, I pull the short url out of the response and set our variable. Lastly, I called window.open giving it the twitter address and setting the status to the title of the page and the shortened url. I've noticed that sometimes window.open will create a new tab, and other times it will create a new window. The last time I ran the extension, it creates a whole new window. I'd much rather it create a tab, but I don't know how to force that.
The last step is to package up the extension for distribution, but I'll let you figure that one out. Google provides a Python script to create the packages, but they say there will be an easy web app to do it in the future.
That's it. We've got a working Twitter extension for Google Chrome. There's definitely some refinement that could be done, but it's enough to give you the big picture on how to build extensions for Chrome. My first impressions of the extension system are pretty positive. I'd like a much easier way to get information about the currently visible document, but I'm sure it's coming in the future. If you've got questions, leave them below or check out the forums. I've attached all the source code so you're more than welcome to install the extension yourself and even make it better.
06/02/2009 - 06:03
Great post, but you've left your API key in the source. I tried it, just to be certain, and I seem to have successfully created http://bit.ly/ey9XB to point to Google.
Many thanks again for an excellent post.
Mark Birbeck
06/02/2009 - 08:44
Thanks for catching that!
06/04/2009 - 02:44
I also created a Twitter extension before seeing this:
http://www.e-x-e.dk/2009/05/30/labs-twitter-add-on-extension-for-google-chrome-new-version-new-post/
In this extension it is possible to write your own message and soon to directly insert information from the selected tab.
07/07/2009 - 13:25
Hey reddest! awesome guide. I just merged your script with Thomases so its possible to shorten URL and fill the twitter bar on the bottom. of CHROME. it is very cool!
10/01/2009 - 04:52
thanx for great post...
gBrowser.selectedTab = createtab;
thil will create a new tab instead of opening a new wimdow.this is working fine with firefox..dont know bout the others
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.