Update on 12/23/2005: The script has been updated to be compatible with Firefox 1.5. See this entry for more information.

Update on 8/28/2005: A bug that prevented the bubble from working correctly once a a conversation had been archived or trashed has been fixed. Please reinstall the script to use this updated version.

Short Version

Want preview bubbles for conversations in Gmail, as shown in the screenshot on the left? Then install the Gmail Conversation Preview Greasemonkey script. You can then right-click on any conversation to see its recent messages in a preview bubble. Greasemonkey 0.5 is required. Should work in Greasemonkey 0.3.5 or 0.5 (neither one has the security issues that plagued earlier versions).

Full Story

One of the things touted by the upcoming Yahoo Mail and Hotmail releases is that they will have a preview/reading pane which will let you see message contents at a glance without having to navigate to an entirely new view. Gmail offers a lightweight version of this already, by showing the first hundred or so characters of each message as a snippet next to the subject. While this is handy for one-liner emails, a full-blown preview pane is often more appropriate.

Given my past experiences with Gmail and Greasemonkey, I figured that adding a preview area to Gmail may just be possible. Ignoring the technical aspects, the first issue was deciding what it should look like. My main issue with traditional preview panes is that they take up a lot of room, even when they aren't needed. Eventually, I was inspired by Google Map's bubbles and decided to try that approach.

I initially triggered the bubbles on mouse hovering, but that was not a good fit: fetching the entire conversation is a heavy-weight operation with some latency while data that appears on hover should be light-weight and display instantaneously. I then tried inserting a small magnifying glass icon within each conversation row that when clicked showed the bubble. However, the icon was too small and hard to click on (Fitts' law and all that). I then considered a keyboard modifier plus mouse click as a trigger, but that seemed like too much effort on the part of the user. In the end, attaching it to right-click seemed like the best choice. Since Gmail doesn't use real links, the contextual menu triggered by the right mouse button is mostly useless, thus overriding it seemed like an acceptable tradeoff. Furthermore, this trigger means that the user does not have to take his/her hand off the mouse. To also facilitate those who use Gmail's many keyboard shortcuts, the V key was made to toggle the preview bubble for the current conversation.

Integrating the preview bubble as smoothly as possible with the rest of the Gmail interface was also a challenge. Given a message ID, it's reasonably easy to fetch its contents (making a GET request for URLs of the form &view=cv&search=all&th=message-id&lvp=-1&cvp=2&qt= ). However, message IDs are not stored in the DOM directly. Instead, it turns out we can leverage Gmail's communication scheme in order to get this information. As others have documented, Gmail receives data from the server in form of JavaScript snippets. Looking at the top of any conversation list's source, we can see that the D() function that receives data in turns calls a function P() in the frame where all the JavaScript resides. Since all data must pass through this global P() function, we can use Greasemonkey to hook into it. This is similar to the trap patching way of extending Classic Mac OS. Specifically, the Greasemonkey script gets a hold of the current P() function and replaces it with a version that first records relevant data in an internal array, and then calls the original function (so that Gmail operations are not affected). Once we have the list of conversations (including IDs) in hand, we can easily map it to its corresponding DOM nodes (each conversation's row has the ID w_message-id ) and show the appropriate bubble.

The script also tries to do clever things by resizing the bubble so that it best fits the displayed messages. Since fetching a conversation implicitly marks it as read, a "Leave Unread" option is provided that actually does a POST request to the server with the appropriate mark as unread command (LiveHTTPHeaders is indispensable for figuring this out). To parse data from a fetched conversation, we grab the appropriate JavaScript text and eval() it while defining the appropriate D() function that extracts the data. In general, the script code is architected the script reasonably cleanly, with a PreviewBubble object with appropriate methods and comments for non-intuitive places, thus it should be ready to hacked on by other people. There are still some rough edges, as well as some drawing bugs in Deer Park that may or may not be my fault. However, I have been using it for the past few weeks and it's very handy when going through lots of email quickly that needs to be read but not necessarily replied to.

(the usual) Disclaimer: I happen to work for Google. This script was produced without any internal knowledge of Gmail, and is not endorsed by Google in any way. If you have any problems with it, please contact only me.