Yesterday, Google announced Google Gears, an open source browser extension that lets web applications work offline (though I think there are more interesting ways to use the platform). Lots of blogs have picked up the story, including TechCrunch, Mashable, and lifehacker. But most simply regurgitate information from Google’s official press release. They don’t explain how the platform works at all.

I’ve spent the morning reading over the Gears documentation, and working through some sample applications. Here are my notes. I’m hoping they’ll provide enough of an overview to get started with Gears without wading through dozens of pages of documentation yourself.

Before I begin, if you haven’t done so already, get started by installing Google Gears now.

What is Google Gears?

The inaugural post on the Google Gears Blog says “Gears is an incremental improvement to the web as it is today. It adds just enough to AJAX to make current web applications work offline.” And they absolutely deliver on that promise.

The Gears toolkit consists of three fairly independent components. Each component improves some challenging aspect of client-side web programming. I’ll briefly describe each component, then get into the details.

  • LocalServer
    Allows a web application to cache resources locally and serve them without a network connection. In other words, it’s a local proxy server with a JavaScript API.
  • Database
    Provides browser-local relational database storage that is accessible through JavaScript. It uses the open source SQLite database system.
  • WorkerPool
    Allows a web applications to run JavaScript code in the background, asynchronously. WorkerPool scripts don’t block script execution on the “main page,” which makes applications more responsive.

That’s it. Why browsers didn’t incorporate these components years ago is a mystery, but now we have them — so how do they work?

How do I use it?

You initialize Google Gears by including the gears_init.js script in your HTML document. You can determine whether Gears is installed by looking for the global google.gears object:

if (!window.google || !google.gears) {
  location.href = "http://gears.google.com/?action=install" +
                  "&message=<your message>&return=<your url>";
}

If Gears isn’t installed this code will redirect your browser to Google’s install page. You can append an optional message and return URL to the install page URL as query string parameters. Once gears is up and running, you’re ready to roll.

Objects are instantiated using the google.gears.factory.create method which takes two parameters: a className and an API version (both of which are strings). To instantiate a LocalServer object you’d use the following code:

var ls = google.gears.factory.create('beta.localserver', '1.0');

That’s pretty much it for the high-level Gears methods. Everything else is handled by the three components I mentioned earlier, so let’s learn more about them.

LocalServer

Instantiating a localserver is easy. I just did it in the example above, so I’m not going to repeat it here (hint: it’s the line that starts with “var ls = “).

The Gears platform enforces the familiar same origin policy that’s common throughout client-side JavaScript. The docs indicate that they’re investigating ways to grant permissions across origins. But, for now, the LocalServer will only allow you to cache pages that have the same scheme, host, and port as the current page.

You interact with the LocalServer through something called a resource store, which is essentially a container of URLs. A resource store can contain any number of URLs, and an application can create any number of resource stores. You can create a simple store by calling the createStore(<store name>) method of a LocalStore. You can retrieve a store that’s already been created by calling using the openStore(<store name>) method, and remove the store (and all the cached resources it contains) by calling removeStore(<store name>). It’s all pretty simple, really. You can create a store called immike as follows:

var ls = google.gears.factory.create('beta.localserver', '1.0');
var store = ls.createStore('immike');

Once you’ve created a store, you can capture a URL by calling

store.capture(<filesToCapture>, <captureCallback>)

Where filesToCapture is either a single URL, or an array of URLs, and captureCallback is a callback function that will be executed after each URL is downloaded. The callback function gets a couple of arguments passed to it: the captured URL, a boolean value indicating whether the download succeeded, and an integer captureId that can be passed to store.abortCapture() to cancel the capture task.

If you have a lot of files to capture and you don’t want to manage them individually, LocalServer supports a second type of resource store called a managed resource store. The methods for creating, opening, and removing a ManagedResourceStore mirror those for an ordinary ResourceStore. You call createManagedStore(), openManagedStore(), and removeManagedStore() to create, open, and remove a managed resource store, respectively. You tell the managed store which URLs to capture by creating a JSON file called a manifest file then setting the manifestUrl attribute to your manifest’s URL. For more info on the manifest syntax, check out the Gears documentation.

That’s about it for the LocalServer, which is the most complicated component to work with. So read on, because the next two components are pretty cool too!

Database

To instantiate a database object you run the following code:

var db = google.gears.factory.create('beta.database', '1.0');

To interact with the relational database you simply open a database connection and execute your SQL:

db.open('my-database');
db.execute('create table if not exists ' +
           'MyTable (MyString varchar(255), MyInt int)');

The execute method supports bind variables. Bind variables are automatically escaped by the SQL engine, improve performance, prevent SQL injection attacks, and are generally A Good Thing. So use them!

You can SELECT data from your new table, using bind variables, as follows:

rs = db.execute('select MyString from MyTable ' +
                'where MyString != ? and MyInt > ?', ['mike', 42]);

The elements of the array ['mike', 42] are bound to the question marks in the SQL statement. They are properly escaped and quoted by SQLite. How convenient.

Once you’ve retrieved your result set you can iterate over the rows using isValidRow(), next(), and close(). For example:

while (rs.isValidRow()) {
  alert(rs.fieldName(0) + ' == ' + rs.field(0));
  rs.next();
}
rs.close();

The field() and fieldName() methods work as expected, returning the value and name of the field at the specified (zero-based) field index. There is also a fieldByName() method which takes the name of a field as an argument, and returns the value of that field for the current row.

Gears adds a column to every table called rowid. The column acts as a unique interger ID for every row in the table. After inserting a new row in a table, the lastInsertRowId attribute of your Database object is updated.

That should be enough to get started. If you’re not familiar with SQLite’s SQL dialect check out SQL As Understood By SQLite over on sqlite.org.

WorkerPool

The third, and final Gears component is the WorkerPool. Like everything else in Gears, it’s instantiated through the google.gears.factory.create method:

var wp = google.gears.factory.create('beta.workerpool', '1.0');

Workers in a WorkerPool can interact using the sendMessage() method, which is available to “child” workers through the global variable

google.gears.workerPool

sendMessage() takes two parameters: the message string to send, and the destination worker’s ID.

When a worker is initialized, it should set the onmessage attribute of the workerPool to a handler method that will be called whenever it receives a message. Once running, workers can send strings to each other, triggering the recipients onmessage handler. The handler receives the string (which can contain complex JSON payloads), along with the workerId of the worker that sent the message.

Workers are created by a “parent” by calling the createWorker() method of a worker pool object. createWorker() returns the worker ID of the newly created worker. The method takes a single string argument, containing the code that the child worker should execute (keep in mind that you can typecast a function to a string in JavaScript, e.g., String(myFunction)).

Here’s a simple example of a worker that evaluates messages sent to it as JavaScript and responds with the result:

var wp = google.gears.factory.create('beta.workerpool', '1.0');
childCode = String(evalHandler) +
            'google.gears.workerPool.onmessage = evalHandler;';

wp.onmessage = parentHandler;

var childId = wp.createWorker(childCode);
wp.sendMessage('2+2', childId);

function parentHandler(msg, sender) {
  var r = document.getElementById('result');
  r.innerHTML = msg;
}

function evalHandler( msg, sender ) {
  var result = eval(msg);
  google.gears.workerPool.sendMessage(String(result), sender);
}

Note that in this example the child sends the result back to the parent, then the parent modifies the document to display the result. The child worker cannot manipulate the DOM directly, since it is executed in as a separate “process” and does not share state with the “parent” script.

Summary

Well, that just about sums it up. My impression is that Gears is a pretty lightweight extension that provides some very powerful tools. If it’s adopted by enough people, Gears has the potential to change the way we think of client-side programming.

The current release is an early beta, so I don’t want to complain too loudly just yet, but… Opera and Safari support would be nice (apparently they’re working on it). Other than that, Gears looks really cool! I’m excited to start playing with it and building some real applications.

If you’ve gotten this far and you still want more, visit the Gears Developer’s Guide, install and run the sample applications, and keep an eye on the Gears Forum, as well as the official blog.