Moving my Gmail Greasemonkey scripts to code.google.com #

Update on 12/17/2012: The scripts now live on GitHub.

I wanted to experiment with code.google.com's project hosting feature, and I thought an easy way to get started would be with my Gmail-related Greasemonkey scripts. This is their new home. I'm not sure how far I'll go with the "project" part (e.g. whether I would ever give anyone else commit privileges, though patches are welcome), but this at least lets me familiarize myself with Subversion.

I've also decided to actually serve all the scripts from the repository, so that there isn't an extra publishing step when making changes (thus far I don't feel the need for stable versions). There is also a feed for all changes, so this should make it easy to stay up to date even if I don't explicitly blog about them. The feed is generated with an XSLT based on Norman Walsh's transform.

I also took this opportunity to make a few improvements to the scripts. The macros one can now also select saved searches when using the "g" keyboard shortcut (contributed by another user). The saved searches script now namespaces the saved data by the current user's email address. This should enable it to work better when there are multiple Gmail users per computer or Google Apps for Your Domain is being used.

Greasemonkey Script Updates #

There have been some reports that my Gmail Macros Greasemonkey script no longer works. I started to look into fixing this, and figured that while I was at it, I should incorporate the improvements that others have made to the script back into the original one. Both posts about it have a growing number of comments, including patched versions with various tweaks. Additionally, there is a Google Group dedicated to Gmail power users that seems to have made even more changes. The script has been updated and now contains the following additional commands:

  • b: Remove label
  • z: Mark unread
  • o: Expand/collapse all messages in a conversation
  • shift-x + a/n/r/u/s/t: Select all/none/read/unread/starred/unstarred
  • h: Show help (reference for built-in and the script's keyboard shortcuts)

Thanks go to Karl, Anand and Michael (?) for these improvements. If I have missed anyone, please let me know.

On a related note, my Google Reader/Gmail integration script has spawned two variants, one by Rob Radez and another by Winston. They went for slightly deeper integrations, if you like the original you may like these too.

Using security holes to work around carrier lock-in #

I recently acquired a Samsung SCH-a990. The QVGA screen and Verizon's reasonably broad coverage for their EVDO network made it seem like a good choice, despite the carrier's tendency to cripple the phone's features (also, a decent camera seemed like a nice perk). Some of the crippling can be worked around, e.g. the lack of Bluetooth file transfer with a cheap MicroSD card, allowing pictures and songs to be moved around without incurring messaging costs.

Verizon HomepageUnfortunately, changing the homepage does not appear to be possible the way it was on my old v710. No one seems to have figured out how to edit the browser/WAP settings on this phone. Users are therefore stuck with the pre-set Verizon homepage (pictured to the left).

A limited amount of customization is allowed, in the form of bookmarks that can be added to the bottom of the page. The bookmarks appear to be pretty limited, allowing only 17 characters for the title, and the links cannot be bound to any access keys, requiring the user to scroll through all the other on-screen links to get to the bookmark ones.

In addition to setting these bookmarks on the phone, there is also a desktop-acessible website where the homepage can be customized. I decided to investigate if sloppy coding on Verizon's side could be used to work around the carrier lock-in.

Sure enough, it turned out that the bookmark editing form did not do any HTML sanitizing for the name field, and neither did the homepage output code. Better yet, the 17 character name limit was only enforced as a maxlength atttribute on the <input> node. By running the following snippet of JavaScript, it was possible to at least bind a bookmark to an access key:

var nameInput = document.forms.theform.tle;
nameInput.setAttribute("maxlength", 200);
nameInput.value='<a href="http://www.google.com/reader/m" accesskey="0">Reader</a>';

Encouraged by this, I tried to see if this hole could be used to hijack the homepage entirely. Unfortunately limitations of the OpenWave browser and the fact that these bookmarks were inserted below the built-in content meant that nothing else seemed to work. For the record, these are the things that I tried:

  • A <style> block that hid all of the images on the page (all of the built-in links appear to be created from images).
  • A <meta> tag to auto-refresh the page to my preferred start page.
  • Positioning the bookmark link absolutely so that it would be overlayed on top of the built-in content.
  • Modifying the item ID parameter in the edit form with those of the built-in links in case they could be edited too.

For now I'm at wits end.

Import your del.icio.us bookmarks into Google #

Update on 12/18/2010: I've updated the tool to handle the V2 Delicious API.

I've written a simple script/site that lets you import your del.icio.us bookmarks into Google's bookmarks feature (which doesn't seem to have a product name - oh, right):

http://persistent.info/delicious2google/

Technical Details

For a while now, Kushal* had been wanting a way to import his del.icio.us bookmarks into Google. There are some advantages to this, including the fact that your tags labels show up in search results if they contained a bookmarked site and one-click Toolbar access. It was pointed out to me that the bookmarks site has a POST/XML-based bulk import interface that the Google Toolbar uses to import IE Favorites. I initially thought that I could use del.icio.us's JSON API to get the list of bookmarks, thus having a completely client-side solution that did not require the user to hand over his/her credentials. Unfortunately that turned out to be limited to 100 posts. Hack-ish workarounds came to mind, like getting the list of tags first and then getting the bookmarks for a particular tag (still wouldn't work if there were more than 100 bookmarks for a single tag). I abandoned that approach, but for the sake of posterity, that version is archived here.

Since del.icio.us also has a REST API that allows full access to data, my next thought was to use that. pydelicious is a pretty simple to use Python library for accessing the API, and it was easy to create a script that used it to fetch the list of bookmarks for a user. Since I already had the code from my previous attempt that took the JSON output and uploaded it, I merely made the Python code output similar JSON and used the same upload method.

This approach seems to work pretty well, but it has the caveat that the user has to enter their username and password into a third-party site. I've provided the source code so that the paranoid can run their own copy. While the Google bookmarks site doesn't have an API per se, it does export its data as RSS feeds thus this does not have to be a one-way transfer; an enterprising hacker could write an importer in the other direction.

* His newly-released luxur(n)y site is interesting if you're in the market for a NYC luxury rental.

Google Reader Redux #

The new version of Reader has been out there long enough (and is now stable enough) that I have some time to catch my breath and make this post (my post-launch post last year came only a couple of days after the big announcement). I've jotted down some of my thoughts from the past few weeks, continuity will not be high.

There were some hints that something big was coming. Chris's Twitter updates were sounding rather intense. Someone in the discussion group inferred from my lack of posts that a major update was imminent (or that I stopped caring - never!). We even invited some bloggers for a sneak peek at the new Reader* but they were nice and respected their embargo.

Reader is in Google Labs, and that puts it in the "throw it against the wall and see what sticks" product family. I'm glad that people seem to have realized that this "throwing" and "seeing" are less passive than they sound. To stretch this metaphor further, if the spaghetti starts to slide off, engineers (and UI designers, and product managers, and others) will study the problem and figure out how to increase its coefficient of friction. Usually the changes are more subtle (witness the myriad of tweaks that have been done to the Google Video homepage) which is perhaps why there is this perception that no post-launch changes are made.

Gmail and Google Reader integrationA lot of people have remarked on the similarities between the new Reader interface and Gmail's. With this in mind, I've created a simple Greasemonkey script that adds a "Feeds" in Gmail. When clicked, Reader's list view is loaded on the right. To install the script (and Greasemonkey if you have never used it before):

  1. Install Greasemonkey from http://greasemonkey.mozdev.org/
  2. Restart Firefox.
  3. Click on the script link above
  4. Click on the "Install" button that's displayed in the upper-right corner of the page.
  5. Visit/reload Gmail

You may wonder why I felt the need to write a Greasemonkey script for my own product. The answer is that integrations are hard and generally require a lot of effort before you can even determine if they are worthwhile. Greasemonkey lets you experiment with UI concepts with minimal effort necessary from either team (I had to make exactly one change to Reader to better support this script, and that was the ability to force list view to be used, even if expanded view is normally selected). I can't really say what, if any, our integration plans are, but enough users have asked for something like this that I thought writing the script was the most expedient way to provide this (unofficial) feature.

I am still subscribed to the "google reader" Blog Search feed, so that is one way to reach the team with feedback. The discussion group is also being monitored, though with the increased volume we now find it hard to respond to a lot of posts. But please keep the feedback coming, it's been great to get direct, concrete indicators for what we should work on next.

* It is rather frustrating to have to call it "the new Reader" or more formally "the new version of Google Reader." It's unfortunate that version numbers are passé, "2.0.1" is a more accurate and concise representation where of where Reader is right now.

Facebook meets Dodgeball #

Dodgeball check-ins in my Facebook profile My initial reaction to the Facebook Mini-Feed/News Feed feature was pretty positive. I still don't think it's such a big deal, but the privacy settings they've added in response to the backlash are pretty well done. Because I felt like this wasn't transparent enough, I thought it would be cool to syndicate my Dodgeball check-ins into my Facebook profile, via their blog (i.e. RSS feed) to notes import feature. It worked pretty well (see picture on the left), and it's sort of neat that these two social networking sites are open even slightly, allowing such co-mingling of data.

Facebook currently allows only one feed to be imported, which means you can't have (say) your check-ins, regular blog and link blog all in there. However, it's possible to create a spliced feed in Google Reader and import that instead.

On a somewhat related note, I've set up a scraped feed for the Facebook Blog, since it doesn't have one for some bizarre reason.

Coincidence? #

Flickr's Geotagging Feature
Flickr's new geotagging feature

Overplot
Overplot

Probably.

overplot: Overheard in New York meets Google Maps #

overplot screenshot I've been a fan of Overheard in New York for a while. At some point, it occurred to me that each quote has a pretty precise location attached to it and that it would be cool to plot all of them on a map. I eventually got motivated enough to actually do it. The result is overplot, a Google Maps API-powered visualization of all of the quotes I could get my hands on.

Technical Details

The most basic issue with implementing this is geocoding all of the location strings (like "Canal & Broadway") to a latitude/longitude pair. When I began this, the best option seemed to be geocoder.us. While it was good enough for prototyping, it quickly became apparent that the quality of data was not good enough. The most severe problem seemed to be that addresses in the southern part of Manhattan were off by half a block to the northwest. Thankfully, a few weeks after I began working on this, Google added a geocoding component to their API. Their geocoder turned out to be much faster and reliable. It is not perfect, but since the set of addresses is pretty tightly constrained, I was able to add some rewriting rules to make the input more easily parsed. As of right now, 54% of the addresses are geocoded. One remaining issue is that some the locations are actually business (like "Saks Fifth Avenue"). I may be able to use the Google AJAX Search API to do local searches for them and get their actual addresses.

I didn't want to directly scrape the HTML of the site to extract all of the quotes. I ended up using the data stored in Google Reader's archive of the site's feed. This allowed me to get at the quotes themselves more easily, without having to worry about the chrome of the site. Going back to October 6, 2005, I had in hand 5,578 quotes at 2838 locations.

Since I had accumulated so many quotes, indicating them with the standard marker object had performance problems since so many can be visible in the same area. This is a well-known problem with the version 2 of the Maps API, and the traditional solution is clustering. However, that doesn't really make sense because when zoomed in at the street level, one has no choice but to display the hundred or so markers that are visible at the same time (the southern part of Manhattan is particularly densely covered).

In the end, my solution was to do my own marker implementation. Instead of each marker being its own overlay, I put all of them in the same overlay (see the QuotesOverlay class). Additionally, I did not split each marker into several layers (shadow, image, click area) - having the shadow be part of the image works well enough. In order to deal with clicks while in the shadow of an info window, I added a global click handler that checks if the click event falls in the boundaries of a marker (see the handleMapClick). Finally, I hide and show markers so that only those bounded by the currently viewed area are displayed. All of this led to a performance improvement by a factor of 8 on Firefox Mac.

I then ran into a user interface problem. Even with marker rendering being efficient, when zoomed out the island of Manhattan becomes a sea of red, which is not very useful. I decided that switching to a neighborhood mode made more sense there. The problem was where to get the neighborhood boundaries. The map used in cabs seemed like a good start, but it made some interesting choices (e.g. TriBeCa was not bounded by Canal St.). New York Magazine's map also turned out to be of dubious quality (e.g. it combined Gramercy and Murray Hill). In the end, the best resource turned out to be the Wikipedia's list. Even there there is some debate (an mild edit war on the souther border of Spanish Harlem - 86th or 96th street - appears to be going on). I'm not entirely satisfied with the current divisions, but they generally make sense.

Once I actually plotted the 28 neighborhoods on the map, I noticed that it was slow to redraw, especially in Firefox. I was using GPolylines to draw the neighborhood outlines, one per area. Possily due to the switch to SVG for polyline rendering, this seemed to have high overheard. In the end, similarly to my solution to the marker performance problem, I switched to a single overlay for all neighborhood outlines. Inside this overlay I used a canvas object to do my own rendering. To deal with MSIE's lack of a canvas implementation, I used the ExplorerCanvas implementation of the interface on top of MSIE's VML API.

The site loads all of the data upfront (in a 728K almost-but-not-quite JSON file). This allowed me to do a simple client-side search that works pretty quickly once all of the quotes are tokenized (done the first time a search is executed).

Update on 12/17/2012: The code, including the scraping/generation tools is now available on GitHub.

Update on 12/1/2013: I have migrated the web UI from v2 to v3 of the Google Maps API (the v2 API was deprecated a few years after overplot was released). However, none of the tools used in data generation (geocoding, neighborhood boundary drawing, etc.) have been migrated. Additionally, I did a minimal migration, and have not investigated if any of the performance hacks that I did in 2006 (e.g. a custom overlay for drawing lots of markers) are still necessary.

Poor Man's Google Reader Search #

My girlfriend is subscribed to a dozen or so cooking/food blogs (using Google Reader). She stars recipes she's interested in, but since there are so many that catch her eye, she now has over one hundred starred items. Finding a recipe from several months ago among them is not easy, since Reader does not support search (yes, we know).

Since these starred items are shared (so my mom can read them too), I thought that I could use Reader's JSON output to allow at least a primitive type of searching. The result is this filtering UI. You can either plug in a public label name (of the form user/user ID/label/label name) or a feed URL and a search term, and all items with titles containing the term will be displayed.

This hack uses the continuation token that Reader provides, so that more than one chunk of items can be fetched.

Smart Google Reader Subscribe Button #

Already subscribed to in Google ReaderA few months ago, Jasper de Vries made a Greasemonkey script that places an unobtrusive feed icon in the upper-right portion of the screen, allowing two-click subscription to feeds that are encountered. Then, today I saw Pete's Greasemonkey scripts wish-list, which included on it a "You’re Already Subscribed To This" script. I figured it would be pretty easy to take Jasper's script and modify it to add this functionality.

My version of the script adds an overlayed check icon when you're subscribed to at least one of the feeds that the site advertises via auto-discovery. This was made easy by a URL that Reader exports: http://www.google.com/reader/api/0/subscribed?s=feed/<feed URL>. It returns "true" or "false" depending on whether the current user (as determined by the Google Account cookie) is subscribed to the given feed.

There are a couple of caveats: The check is not foolproof - sites that use FeedBurner, that have changed their feed URLs since you've subscribed, or that available under different URLs (e.g. with or without www) may not be detected correctly. Additionally, by its very nature, the script reports to Google Reader's servers all the feed URLs that it encounters. The extra-paranoid may not like this. However, an alternative approach (download the entire list of subscriptions, cache it, and use that for checks) would have meant a lot more coding on my side, more than I'm willing to do on a Saturday afternoon.

Export Google Personalized Homepage Feeds to OPML #

Update on 2/1/2010: iGoogle no longer supports inline gadgets, which includes this module. The alternate OPML export method described by Google Operating System still works however.

The recently released Google Reader homepage module makes it even easier to keep track of feeds on the Google Personalized Homepage. Unlike the built-in feed handling of the homepage, it keeps track of read state, can display item contents inline (in a floating bubble) and takes up much less room. However, if you've been using the homepage's feed modules, moving the feeds to Reader is a tedious process, since the site does not provide an export functionality.

I've created a simple inline module that adds an OPML export feature to the hompage. It does this by going through the DOM, looking for feed modules, and extracting the feed URL from them. It also handles Google News modules, which can be mapped to feeds, but are not quite the same as far as the code goes. To use it, click the "Add to Google" button below:

Add to Google

Once you've gone through the module adding process (and allowed inlining), simply click the "Export OPML" button. This does not work in Internet Explorer, since it does not support data: URIs. Then, do a "Save As..." on the pop-up window and import that OPML file into Google Reader (click "Edit Subscriptions" then "More Actions..." then "Import Subscriptions") or any other feed reader.

This module is not endorsed by Google, and is not guaranteed to always work (especially on non-US Google domains).

Changing the Homepage on the Motorola v710 #

Like most other carriers, Verizon has crippled its phones (in this case the Motorola v710) to better suit its services and partener deals (incidentally, I finally got my $25 credit from the class action settlement involving the v710's crippled Bluetooth). Most annoyingly, it does not provide a way to change the homepage. The default homepage is some joint venture with MSN that contains lots of images, loads slowly, and is generally useless. It has a bookmarks sub-page, but it's annoying to get to and use (e.g. none of the bookmarks have access keys).

Thankfully, it looks like there is a hidden way to change the homepage. This page incidentally mentioned this while talking about how to use your own proxy with the phone. Specifically, the steps are:

  • Go to the menu (upper middle button)
  • Quickly type 073887*
  • Use 000000 (that's 6 zeros) as the security code
  • Select "Web Sessions"
  • Select the "VZW" entry (it should be locked)
  • From the menu, select "Copy Entry" and name your copy (the name does not matter)
  • Edit your copy and change the homepage field as you see fit
  • Select the entry and pick "Set Default" from its menu

I initially considered something like the mobile Google Personalized Homepage, but I decided that was too heavyweight. Instead I created my own mini start-page has that is less than 1K in size.

410 Annoying #

Russell has stopped blogging, and seems to want people to unsubscribe from his feed. Not content to only return a 410 Gone status code*, he has also inserted a seizure-inducing image into a item with an ever-changing GUID. I'm not sure if this falls under "clever social hack" (since not all aggregators support 410 - including Reader to some degree) or "ugly perversion of HTTP."

* The Reader crawl logs suggest that initially his "unsubscribe now" feed was returning a 200 status code, which made this less interesting.

Expanding Gmail's Filter Input #

Gmail filter screenshot Gmail has a seemingly simple but actually powerful filtering system. The "Has the words" input field can be used for arbitrary queries. However, the one line input is very cramped when trying to filter multiple mailing lists at once. A textarea would be more appropriate.

Initially, this seemed like a job for Greasemonkey. However, since the filter input form is created dynamically, it's hard to detect when it's visible without resorting to polling. Instead, it seemed easier to find the right node with a CSS selector like input[name="cf1_has"]. Then, XBL can be used to replace the input node with a textarea node (styled appropriately). All that's really necessary is to insert the following in userContent.css:

@-moz-document domain(mail.google.com) {
  input[name="cf1_has"] {
    display: none;
    -moz-binding: url(http://persistent.info/files/gmail-filter.xml#filter);
  }
}

The XBL file is pretty straightforward (it didn't look like I could use the <content> element to insert the textarea, since that would've placed it as a child of the input node).

For the paranoid (or merely careful), it's best to make a local copy of the XBL file, otherwise you're always going to be loading code from my site. I tried to use a data: URL, to make things self-contained, but it didn't seem to work.

Firefox 1.5.0.2 and Greasemonkey #

Firefox 1.5.0.2 was just released. This contains a bug fix (perhaps bug 265740, perhaps this security issue, perhaps something else) that gets rid of the crashes when using my Greasemonkey scripts, especially label colors and conversation previews. I have been running with all scripts enabled for the past couple of days and have not had any crashes. Hopefully the same is true for everyone else.

I have also updated the preview script with the latest Gmail IDs, in a similar manner that the macros script was updated.

Google Reader Tidbits #

Google Reader recently launched sharing, a feature that I had a hand in. I've used it to power my link blog, available in the sidebar (only on the front page). Although these sharing "clips" are easiest to use when pre-styled with one of our color schemes, you can choose the "None" option and then use your own CSS to make them blend in with the rest of your site, as I've done. And since it's all JSON underneath, you really use a public label any way you want on your site.

What may not be obvious about this sharing feature is that it can be used together to splice feeds. Furthermore, you can chain shared labels. For example, the "Stuff written (or recommended) by the Reader team" section in the Google Reader blog was put together like this (arrows indicate subscriptions/labels being applied):

Sharing flow

This way, everyone gets to control their own "me" label, without having to modify the team account when wanting to add/remove feeds.

While developing this sharing feature, it became clear that the ultimate origin of an item in a feed is very important (i.e. I may see it because I'm subscribed to your "web-dev" label, but really it's from QuirksBlog). We joked about the need for a "Molecule" format that would specify the aggregation of multiple Atom feeds. We even began coding a (namespaced) origin element that would contain the title, id, homepage URL, etc. of the originating site for this item. Then, while re-reading RFC 4287 for another reason, we came across the source element in Atom, which does exactly what we had set out to (re)implement. This tells you two things:

  1. The Atom people were pretty clever to have foreseen this use case.
  2. No matter how well your spec/documentation is written, people will still miss things (a.k.a. everyone is a bozo at some point).

Now that we produce feeds for others to consume, it's nice to use Atom to its fullest so that we can validate (there are some scary looking warnings that we'd like to fix, but as of now that feed is in fact valid Atom 1.0).

Updated Gmail Macros Script #

As some commenters had noticed, Gmail changed the DOM node ID it uses for the action menu, which broke my Gmail Macros Greasemonkey script (since that's how it dispatches commands to the Gmail UI). I have now updated the script to work with both new and old IDs.

Additionally, based on my experience developing the Google Reader labels selector, I've gotten rid of a harmless exception that would show up when using the label selector in this script (bug 236791 has more details).

Update on 3/25: The script now also recognizes the "Contacts" section as a destination when using the "g" shortcut.

Rendering Text Inside the Canvas Object #

I recently had an idea for a cool hack involving the <canvas> tag/object that is supported by Safari 1.3/2.0, Firefox 1.5 and Opera 9.0. However, I quickly realized that the object does not support text rendering, which made it seemingly useless. The WHATWG spec had this to say:

// drawing text is not supported in this version of the API
// (there is no way to predict what metrics the fonts will have,
// which makes fonts very hard to use for painting)

I'm not entirely sure why predictable font metrics are necessary (for pixel-perfect compliance testing I suppose), but the situation doesn't seem too hopeful. Mozilla's solution to this was the drawWindow, function that could be used with an iframe to render text. However, creating arbitrary windows for text rendering seemed like a lot of overhead, and I wanted something that worked in all browsers.

I remembered from my OpenGL hacking days that a similar shortcoming existed in that environment, and that there were a few workarounds available. One was to render fonts yourself, using the basic line/arc primitives to rasterize TrueType/PostScript fonts. However, this meant finding a font and mapping its drawing operations to canvas ones, which seemed like more work than I was willing to put in for a quick hack. Additionally, having to draw many complex strokes per letter seemed like it would impact performance.

The alternative was to use a font texture. This is usually an image composed of all the necessary letters and symbols for a font. By drawing pieces of it one after another, words can be composed. Since the font has been already rasterized into the texture image, it shouldn't matter how complex each letter is. This also mapped well onto the drawImage call supported by the canvas 2D context. Some quick Googling turned up a ready-made font texture and (more importantly) a table of character coordinates positions within it. If doing this from scratch, Bitmap Font Builder looks handy.

This is the result of putting all of that together. It has decent performance in Safari and Opera 9.0, but Firefox can only manage about ten frames per second. It was even slower when I used drawImage() with an image object. I can only assume that Gecko will decompress the image for each function call instead of keeping the raw pixels around. Thankfully there is an overloaded version of the function that accepts a canvas object. By rendering the desired image into a canvas first and then passing that, performance improved significantly. However, Safari does not seem to support canvas objects as arguments for drawImage(), so a bit of browser detection is necessary.

Update on 2/27/2006: I was curious about the drawImage() performance in Firefox with images vs. canvases that I decided to do a more thorough investigation. Using a simple test bed, I measured the speed of various image rendering calls:

  • drawImage() with an image argument
  • drawImage() with a canvas argument
  • Creating a pattern with an image and then using fillRect() with it
  • Creating a pattern with a canvas and then using fillRect() with it

I then ran 50 iterations of each in Firefox 1.5, Safari 2.0 and Opera 9.0 preview 2, all on a dual 2.3 Ghz G5, with these results:

Method 8-bit opqaue GIF 8-bit transparent GIF* 8-bit opaque PNG* 8-bit transparent PNG* 24-bit opaque PNG 24-bit transparent PNG JPEG
Firefox 1.5
drawImage w/ image 74 138 593 574 242 1959 227
drawImage w/ canvas 446 4378 4433 4444 443 495 454
fillRect w/ pattern w/ image 10 22 75 33 32 118 27
fillRect w/ pattern w/ canvas err err err err err err err
Safari 2.0
drawImage w/ image 15 27 97 34 47 123 62
drawImage w/ canvas NV NV NV NV NV NV NV
fillRect w/ pattern w/ image NV NV NV NV NV NV NV
fillRect w/ pattern w/ canvas err err err err err err err
Opera 9.0 preview 2
drawImage w/ image 521 273 3313 880 1651 4007 err
drawImage w/ canvas 3817 37612 37186 38024 3753 3862 err
fillRect w/ pattern w/ image 3773 36019 36735 37303 3709 3571 err
fillRect w/ pattern w/ canvas NV NV NV NV NV NV err

* 500 iterations
NV: No visible output (but no exceptions thrown either)

As it can be seen, the Firefox performance boost that I saw with drawImage() and a canvas argument only occurs with 24-bit PNGs with an alpha channel. In general, using a pattern is the fastest way to draw an image in Firefox. The one trade-off is that you don't get to use scaling (by playing with the source/destination rectangles), but that can be accomplished with the global matrix transform anyway. Since paterns are always drawn beginning at the top/left corner of the target rectangle, some use of clipping is necesary if only a portion of the image is necessary. However, even with clipping, the use of patterns brings Firefox speed in the text rendering test to ~36 fps instead of ~10 fps.

The Opera numbers are much lower than the others because Opera seems to do some event handling and extra screen refreshes during the benchmark. In general, the fastest approach in Opera is to use drawImage() with a canvas object. Safari seems to have the most trouble supporting alternate approaches, presumably because it had the earliest implementation of canvas and the spec didn't actually exist at that point.

Google Search History as RSS #

Update on 4/24/2006: The feeds have now been officially announced (i.e. they are exposed via auto-discovery on the page). HTTP Basic Authentication (over SSL) is now also supported (in addition to the cookie).

Google recently released some Dashboard Widgets, among them one for accessing your search history. Until now, it had only been accessible at its homepage, so I wondered how the widget got that data out. Thankfully the widget code was not obfuscated, and I was able to see snippets like the following:

  Widget.feed = "http://www.google.com/searchhistory/find?zx=" + 
              randomString() + "&num=50&output=rss&client=google-mcsmhwidget";
  ...
  var url = Widget.feed;
  url += "&start=" + Widget.resultsStart;
  url += "&q=" + encodeURIComponent(query);

Sure enough, URLs such as this one bring up a search through my search history as an RSS feed. The query part of the URL can be left blank to show all items. I'm guessing that by judicious use of the start and num parameters, one could even get at ones entire search history. Presumably the attention fanboys will like this.

The key in the above URL seems to be the output=rss parameter. Since bookmarks are in the same UI as search history, perhaps they can be viewed as RSS too? Yes, they can (though with some XML errors that the team is aware of as of 3/31/2006 it's well-formed XML ). The Trends page however doesn't work as RSS.

Note that these feeds are not really useful for most aggregators, since they require you to logged in to your Google Account and be authenticated by a cookie. The one exception might be Firefox Live Bookmarks. By creating one pointed to your searches or bookmarks feeds and putting it your toolbar, you have one-click access to your search history or bookmarks. However, the real use of the feeds is as a pseudo-API, as they are used in the Dashboard Widget.

This might seem like a convenient "leak," but it's something I decided to blog about for myself, without any prompting from the search history team (though they did get a heads-up about this post). The feed URLs and format may change at any time, though they probably won't deviate too much unless there's a good reason.

Interviewing at Google #

Someone recently asked me if I had any tips about interviewing at Google. Surprisingly enough, even though our jobs page mentions all the great perks and things you could be doing at Google, it doesn't say much about the process. However, I did come across this "What to expect from your Google interview" page created by our Zurich office. Although it's nominally aimed at European candidates, nearly all the information contained within is applicable to all our offices. In addition to emailing that person a link, I'm blogging about it, so that others may benefit as well.

Birthday #

Date & Time Google Homepage Module #

Date & Time module The sample Date & Time module for the Google Personalized Homepage is pretty, but it takes up a lot of room. My mom (hi mom!) wanted something that was a bit more compact, with an always visible calendar, and the ability to view months in the past/future. I've whipped up a modified version of the original module that does this. It's also available at the excellent (unofficial) Google Modules site.

To install it:

  • Visit your Personalized Homepage
  • Click the "Add Content" link in the upper-left corner
  • Type in the URL http://persistent.info/modules/datetime.xml in the "Create a Section" form and press "Go"