OilCan: Greasemonkey on
steroids for Android

OilCan lets you customize any website by inserting JavaScript to change the website and help it reach into the Android world using intents.

OilCan inserts some powerful buttons into normal websites, and that power comes from Android intents. We didn't write a barcode scanner or the radar app into OilCan, but using intents we can launch those apps with parameters. We can request the Barcode Scanner app to scan something and return the code to us, or launch the Radar app with a specific lat/lon.

Userscripts can do other cool stuff, like hide the navigation columns in Wikipedia to make it easier to view on small screens. This is a proof-of-concept for now, and will probably turn into a binary plugin for the default Browser in the future.

Vimeo is being a bit odd with videos that don't have sound. Start playing the video, then drag the seek bar slightly to bring it back to normal speed. Or let it play through the video fast and press play again to watch at normal speed.

How does this work?

OilCan is a thin layer on the WebKit browser that comes with Android. As you browse websites, it inserts custom JavaScript for any pages that match a URL pattern. Here's a script that would insert a "Scan Barcode" button like the one shown earlier:

// ==UserScript==
// @name          Scan barcode into Half.com
// @description   Add button to Half.com search box to scan barcode
// @author        Jeffrey Sharkey
// @include       http://*m.half.com*
// ==/UserScript==

function generate(item) {
	var helper = document.createElement('input');
	helper.type = 'button';
	helper.value = 'Scan barcode...';
	helper.addEventListener('click', function(event) {
		// use the intentHelper bridge to fire an intent to Barcode Scanner
		// it's available in Market, or from http://code.google.com/p/zxing/
		var result = window.intentHelper.startActivityForResult(JSON.stringify({

		// parse the result we get back, and read the barcode from the extras
		result = JSON.parse(result);
		item.value = result['extras']['SCAN_RESULT'];
	}, false);
	return helper;

// find the 'query' form field
var items = document.body.getElementsByTagName('input');
for(i in items) {
	var item = items[i];
	if(item.name == 'query') {
		// build our 'scan barcode' helper button
		// then insert it after the query form field
		var helper = generate(item);
		item.parentNode.insertBefore(helper, item.nextSibling);

How can I use my own scripts?

Some existing Greasemonkey userscripts may just "work" because WebKit provides excellent JavaScript DOM access. However, one missing piece that may prove frustrating is the document.execute() XPath query engine.

There are some excellent guides on writing userscripts, and a few examples are included in the source dump.

You can install your own scripts by loading from the SD card, or by browsing to them. In either case, the URL/file needs to have the extension ".user.js" to be recognized. Most script parameters are defined in the comment block, just like normal Greasemonkey scripts.

What about security?

The window.intentHelper bridge helps us launch Android intents from JavaScript inserted into any website. But how can we make sure that a malicious website doesn't launch intents?

One approach is to give inserted userscripts a special magic key that is required when launching intents. Before inserting a userscript, we add the magic key to any startIntent() and startIntentForResult() calls. Because we wrap the insertion as an anonymous JavaScript function, and use addEventListener() when making calls, this should successfully hide the magic key from any malicious JavaScript in the original page.

Background on Greasemonkey

GreaseMonkey is an awesome plugin for Firefox that inserts custom JavaScript into websites, usually to help fix little quirks that might bug you. There are thousands of GreaseMonkey scripts, like one that puts favicons into Google Reader, or one that wraps Google search results into two columns.

All original material Copyright 2008 Jeffrey Sharkey and released under Creative Commons BY-NC-SA license for documentation and GPLv3 for code.