Work takes my programmers time, so anytime I think I can do something rather quickly, I’d try and make an effort. Since I’ve made Google Chrome my default browser, I thought I’d sink my teeth into some Chrome Extensions work – I attempted something similar with Mozilla Firefox, but found the whole development process to be clunky. Having a look over the API documentation, with Chrome you can build something up rather rapidly with no browser restarts. Sometimes, you don’t even need to reload the extension!

The problem

So, I went about thinking about what it is I do all the time that I could simplify. My immediate thought was posting links and pictures to Twitter. It’s something I regularly do, and so something I’d make easier. The process is rather complicated:

  1. Copy the link.
  2. Go to http://j.mp.
  3. Paste the link, hit Shorten.
  4. Click the Copy button.
  5. Go to Twitter (it’s usually open on another tab).
  6. Type in my message, paste the link.

Of course, if I used something like TweetDeck, the process is rather easier, copy the link, go to TweetDeck, paste the link. But, why not just do it in the browser?

The solution

So, after seeing what I could do with Chrome, I made a plan,

  • A button on the toolbar will pop open a box that has the address field and a text box to type your tweet in.
  • Have a photo-picker mode so I can insert the address of the image if needed.

And in the background,

  • Have a Popup page with an associated icon.
  • Have an options page to configure Twitter.
  • Try and not use something like jQuery, it’s all rather simple.

The first problem was dealing with OAuth. In reading the internet, I figured it is probably best to also have a Background page – the background page is actually rather useful in storing shared functions across the extension, and to help with messaging. I’ll write more about OAuth in another blog post, and more on messaging just a bit later.

So, I have an options page that goes into the Background page to call some OAuth magic, and when it gets the required tokens, it will save them into local storage. Local storage is a new HTML5 key/value store, much like cookies, but also much more powerful. You can simply do this,

var myValue = "not a cookie";
localStorage["cookie"] = myValue;
alert(localStorage["cookie"]);

localStorage.removeKey("cookie");
if (!localStorage["cookie"]) {
	alert("The cookie is no more.");
}

Friggin’ sweet!

So, with that, I can have a function in my background page to check if the user is logged in or not,

var userData = {
	screenName:	localStorage["screenName"],
	userId:		localStorage["userId"]
};

function IsLoggedIn() {
	if (!userData.userId || userData.userId == 0) {
		return false;
	} else {
		return true;
	}
}

After logging in, we need a page to do the sending. This was actually the first thing I did,

The file, manifest.json, at the root of the extension directory, is where all this magic happens,

"browser_action": {
	"default_icon": "icon19.png",
	"default_title": "Send to Twitter",
	"default_popup": "html/popup.html"
},

After some playing around I finally got something looking like this,

To get the address of the currently selected tab was easy as,

function applyCurrentTabToAddress() {
	var currentTab = chrome.tabs.getSelected(null, function(tab) {
		addressText.value = tab.url;
	});
}


(There was a bit more code in there to do some URL checking).

Almost there! Now to just send it!

function sendMessage() {
	var message = messageText.value;
	var address = addressText.value;
	bg.PostLinkToTwitter(message, address);
	window.close();
	return false;
}


bg is a variable I defined earlier as a shorthand for the background page, (it’s just var bg = chrome.extension.getBackgroundPage();). In the background page I do an AJAX request to the Bitly API to shrink the URL, and then another AJAX request via OAuth to Twitter. There is a little badge animation too, simply set up using an interval and chrome.browserAction.setBadgeText({ text: currentBadge});.

To do the final thing, the pop up to indicate the job has completed, I use the WebKit Desktop Notification API. So long as you have requested permission in the manifest, you can make a desktop notification.

function notifyComplete(message) {
	var notify = webkitNotifications.createNotification('',
		'Just sent to Twitter', message);
	notify.show();
	setTimeout(function() {
		notify.cancel();
	}, 4000);
	chrome.browserAction.setBadgeText({text: ""});
}


Notice the timeout to hide the notification window. Desktop notifications don’t have a set timeout period (stupid idea, but it is rather new), so you have to do it yourself.

Now, next thing I wanted to do was allow for you to chose a picture on the page to also shorten. I first thought you could click a small link, then all images on the page will become “hot,” click the picture and the popup reappears. Unfortunately, you cannot programmatically call the popup to open, so that was out.

The solution was to inject a content script on the page. This requires the "http://*/*" permission in the permissions array of the extension manifest. Yeah, that’s saying that it can access any website.

To only have it load when required, I have the following function,

function injectImagePicker() {
	chrome.tabs.executeScript(null, {file: "js/imagePicker.js"});
}


That injects the script onto the current tab. The script is very simple,

if (window == top) {
	var myImages = findImages();
	chrome.extension.sendRequest({method: "get_images",
		result: myImages});
}

function findImages() {
	var images = document.getElementsByTagName("img");

	var output = [];
	for (var item in images) {
		if (typeof images[item].src != "undefined") {
			if (output.indexOf(images[item].src) == -1) {
				output.push(images[item].src);
			}
		}
	}
	return output;
};


In the third line of that code block, is the sendRequest call, which is part of the messaging API. It’s rather simple, that line will send a request to the background page with the JSON.

On the background page, I just have to add a listener to the onRequest event with a callback function,

chrome.extension.onRequest.addListener(
	function (request, sender, sendResponse) {
		if (request.method == "get_images") {
			getImageCallback(request.result);
		}
		sendResponse({}); // we don't want to do anything
	}
);


The final sendResponse will close the message pipe. getImageCallback is a callback function that the popup sets on the fly with the background page. We do this because sending messages to other pages isn’t as easy. We get our little array of images and splat them out on the popup.

Finally, you go into Manage Extensions (tip, right-click the button to get quick access to it), and in the Developer Mode box hit the Pack Extension button.

The end result,

Really, the only difficult part of this whole exercise was the OAuth stuff. If you know HTML and Javascript, you’re pretty well placed for Chrome extension work.

You can download the extension from here, but I still have a lot of tidy-up work to do, especially in the Javascript department, before I submit it to Chrome.

blog comments powered by Disqus