Thunderbird Jetpack Teasers: Custom tab with wmsy contents

Example jetpack source here because it’s too much to inline.
tool-building and visualization run amok

Example jetpack source here because it’s too much to inline.
jetpack.future.import("thunderbird.compose"); jetpack.thunderbird.compose.appendComposePanel({ onReady: function (panel, composeContext) { let doc = panel.contentDocument; let msgNode = $("<span />", doc.body).appendTo(doc.body); let started = Date.now(); setInterval(function() { let words = composeContext.getPlaintextContents().split(/\s+/); let secs = Math.ceil((Date.now() - started) / 1000); let wordsPerMinute = Math.floor((words.length * 60) / secs); msgNode.text(wordsPerMinute + " words per minute."); }, 1000); panel.show(); }, html: <><body style="overflow: hidden"></body></> });


As far as I know (and ignoring my previous efforts on chroniquery along these lines), up until now you had your C/C++ Mozilla backtraces via gdb (chocolate) and your JS backtraces via “call DumpJSStack()” or the debugger keyword from within JS (peanut butter), but these two great flavors had never come together to make a lot of money for dentists.
The screenshots (which is actually just one screenshot split in two) show invocation of a custom python gdb command building on my previous exciting pretty gdb commands. The command has filtered out boring JS interpreter / XPConnect code and interleaved exciting interesting JS stack frames.
The implementation is reasonably simple and intended to be able to be implemented using VProbes to support my recent performance work along those lines. We walk stack frames the usual way. Ahead of time, we have marked out the PC ranges of interesting JS interpreter functions (js_Interpret and js_Execute). If the stack frame’s instruction pointer is in one of those functions we grab the JSContext argument. We pop frames until we reach the native frame those functions allocate from their own stack space (whose boundaries we know from the stack walking).
There is one trick we have to do involving dormantFrameChain. While js_Execute has a consistent and straightforward usage of JS_SaveFrameChain, XPConnect and its quickstub friends are more complex. Right now we use a dumb heuristic that just looks if our frame pointer is 0 and there is a dormantFrameChain, and in that case we restore it. (Thankfully the garbage collector needs to know about the shelved frames, otherwise we might have to chase frames down.) I haven’t put much effort into thinking about it, but the heurstic seems a bit reckless. We could likely just concurrently walk the XPConnect context stack to figure out when to restore dormant frame chains. The existing VProbe JS stack (only) code already goes to the horrible effort to get at the thread-local stack, so it wouldn’t be too much more work. Things probably also fall down during garbage collection right now.
Hg repository is here. Under no circumstances try to use this with jblandy’s excellent archer-mozilla JS magic right now. The current code is very distrustful of gdb.Value in a dumb way and does exceedingly dangerous things wherein pointers are bounced to strings and back to integers because direct integer coercion is forbidden. With pretty printers installed this is likely to break. Also, this is all only tested on 1.9.1.

Following in the footsteps of the MIT SIMILE project’s Exhibit tool (originally authored by David Huynh) and Thunderbird Seek extension (again by David Huynh), we are hoping to land faceted global search for Thunderbird 3.0 (a la gloda) in beta 4.
I think it’s important to point out how ridiculously awesome the Seek extension is. It is the only example of faceted browsing or search in an e-mail client that I am aware of. (Note: I have to assume there are some research e-mail clients out there with faceting, but I haven’t seen them.) Given the data model available to extensions in Thunderbird 2.0 and the idiosyncratic architecture of the UI code in 2.0, it’s not only a feature marvel but also a technical marvel.
Unfortunately, there was only so much Seek could do before it hit a wall given the limitations it had to work with. Thunderbird 2.0’s per-folder indices are just that, per-folder. They also require (fast) O(n) search on any attribute other than their unique key. Although Seek populated an in-memory index for each folder, it was faced with having to implement its own global indexer and persistent database.
Gloda is now at a point where a global database should no longer be the limiting factor for extensions, or the core Thunderbird experience…
The screenshots are of a fulltext search for “gloda” in my message store. The first screenshot is without any facets applied and me hovering over one of David Ascher’s e-mail address. The second is after having selected the “!action” tag and hovering over one of David Bienvenu’s e-mail address. Gloda has a concept of contact aggregation of identities but owing to a want of UI for this in the address-book right now, it doesn’t happen. We do not yet coalesce (approximately) duplicate messages, which explains any apparent duplicates you see.
The current state of things is a result of development effort by myself and David Ascher with design input from Bryan Clark and Andreas Nilsson (with hopefully much more to come soon
. Although we aren’t using much code from our previous exptoolbar efforts, a lot of the thinking is based on the work David, Bryan, and myself did on that. Much thanks to Kent James, Siddharth Agarwal, and David Bienvenu for their recent and ongoing improvements to the gloda (and mailnews) back-end which help make this hopefully compelling UI feature actually usable through efficient and comprehensive indexing that does not make you want to throw your computer through a window.
If you use linux or OS X, I just linked you to try server builds. The windows try server was sadly on fire and so couldn’t attend the build party. The bug tracking the enhancement is bug 474711 and has repository info if you want to spin your own build. New try server builds will also be noted there. Please keep in mind that this is an in-progress development effort; it is not finished, there are bugs. Accordingly, please direct any feedback/discussion to the dev-apps-thunderbird list / newsgroup rather than the bug. Please beware that increases in awesomeness require that your gloda database be automatically blown away if you try the new version. And first you have to turn gloda on if you have not already.
The greatest problem with performance profiling is getting as much information as possible while affecting the results as little as possible. For my work on pecobro I used mozilla’s JavaScript DTrace probes. Because the probes are limited to notifications of all function invocations/returns with no discretion and there is no support for JS backtraces, the impact on performance was heavy. Although I have never seriously entertained using chronicle-recorder (via chroniquery) for performance investigations, it is a phenomenal tool and it would be fantastic if it were usable for this purpose.
VMware introduced with Workstation 6/6.5 the ability to efficiently record VM execution by recording the non-deterministic parts of VM execution. When you hit the record button it takes a snapshot and then does its thing. For a 2 minute execution trace where Thunderbird is started up and gloda starts indexing and adaptively targets for 80% cpu usage, I have a 1G memory snapshot (the amount of memory allocated to the VM), a 57M vmlog file, and a 28M vmsn file. There is also and a 40M disk delta file (against the disk snapshot), but I presume that’s a side effect of the execution rather than a component of it.
The record/replay functionality is the key to being able to analyze performance while minimizing the distortion of the data-gathering mechanisms. There are apparently a lot of other solutions in the pipeline, many of them open source. VMware peeps apparently also created a record/replay-ish mechanism for valgrind, valgrind-rr, which roc has thought about leveraging for chronicle-recorder. I have also heard of Xen solutions to the problem, but am not currently aware of any usable solutions today. And of course, there are many precursors to VMware’s work, but this blog post is not a literature survey.
There are 3 ways to get data out of a VM under replay, only 2 of which are usable for my purposes.
My first step in this direction has been using a combination of #1 and #2 to get javascript backtraces using a timer-interval probe. The probe roughly does the following:
The obvious-ish limitation is that by relying on XPConnect’s understanding of the JS stack, we miss out on the most specific pure interpreter stack frames at any given time. This is mitigated by the fact that XPConnect is like air to the Thunderbird code-base and that we still have the functions higher up the call stack. This can also presumably be addressed by detecting when we are in the interpreter code and poking around. It’s been a while since I’ve been in that part of SpiderMonkey’s guts… there may be complications with fast natives that could require clever stack work.
This blog post is getting rather long, so let’s just tie this off and say that I have extended doccelerator to be able to parse the trace files, spitting the output into its own CouchDB database. Then doccelerator is able to expose that data via Kyle Scholz’s JSViz in an interactive force-directed graph that is related back to the documentation data. The second screenshot demonstrates that double-clicking on the (blue) node that is the source of the tooltip brings up our documentation on GlodaIndexer.callbackDriver. doccelerator hg repo. vprobe emmett script in hg repo.
See a live demo here. It will eat your cpu although it will eventually back off once it feels that layout has converged. You should be able to drag nodes around. You should also be able to double-click on nodes and have the documentation for that function be shown *if it is available*. We have no mapping for native frames or XBL stuff at this time. Depending on what other browsers do when they see JS 1.8 code, it may not work in non-Firefox browsers. (If they ignore the 1.8 file, all should be well.) I will ideally fix that soon by adding an explicit extension mechanism.
As part of our goal to make it easy to write extensions for Thunderbird 3, we’ve been working on getting Jetpack running under Thunderbird and exposing Thunderbird-specific points. This is all experimental, but it’s having good results.
The first example replaces the message you get from twitter when someone follows you and instead shows you that person’s twitter page so you can see what they’ve written. Unfortunately, if you try and click on links on the page you will become sad because they all try and trigger your web browser. But Standard8 is hard at work resolving the content display issues. Besides demonstrating registration via a regex over the sender’s e-mail address, it also shows us extracting message headers from the message. Also, we introduce a small HTML snippet that precedes the nested web browser so it’s not just an embedded web browser.
jetpack.future.import("thunderbird.messageDisplay"); jetpack.thunderbird.messageDisplay.overrideMessageDisplay({ match: { fromAddress: /twitter-follow-[^@]+@postmaster.twitter.com/ }, onDisplay: function(aGlodaMsg, aMimeMsg) { let desc = aMimeMsg.get("X-Twittersendername", "some anonymous jerk") + " has followed you on Twitter. Check out their twitter page below."; return { beforeHtml: <> <div style="background-color: black; color: white; padding: 3px; margin: 3px; -moz-border-radius: 3px;"> {desc} </div> </> url: "http://twitter.com/" + aMimeMsg.get("X-Twittersenderscreenname") }; } });
Our second example of the extension point replaces e-mails from Amazon about an order (order confirmation and shipment confirmation) with the amount of money you spent on the order in BIG LETTERS (or rather BIG NUMBERS). It uses a regular expression run against the message body to find the total order cost. Then it generates a simple web page to present the information to you.
jetpack.future.import("thunderbird.messageDisplay"); jetpack.thunderbird.messageDisplay.overrideMessageDisplay({ match: { fromAddress: /(?:auto-confirm|ship-confirm)@amazon.(?:com|ca)/ }, _totalRe: /Total(?: for this Order)?:[^$]+\$\s*(\d+\.\d{2})/, onDisplay: function(aGlodaMsg, aMimeMsg, aMsgHdr) { let bodyText = aMimeMsg.coerceBodyToPlaintext(aMsgHdr.folder); let match = this._totalRe.exec(bodyText); let total = match ? match[1] : "hard to say"; return { html: <> <style><![CDATA[ body { background-color: #ffffff; } .amount { font-size: 800%; } ]]></style> <body> you spent... <span class="amount">${total}</span> </body> </> }; } });
The modified version of Jetpack can be found here on the “thunderbird” branch. “about:jetpack” can be triggered from the “Tools” menu. Besides the development jetpack, you can also add jetpacks from the about:jetpack “Installed Features” tab by providing a URL directly to the javascript file. Unfortunately, I just tried installed more than one Feature at the same time and that fell down. I’m unclear if that’s a Thunderbird content issue, a problem with my changes, or a problem in Jetpack/Ubiquity that may go away when I update the branch.
About the name. David Ascher picked it. My choice was flamboydoc in recognition of my love of angry fruit salad color themes and because every remotely sane name has already been used by the 10 million other documentation tools out there. Regrettably not only have we lost the excellent name, but the color scheme is at best mildly irritated at this point.
So why yet another JavaScript documentation tool:
1: Right now I only support commenting at the documentation display granularity which means you cannot comment on arguments individually, just the function/method/class/etc.
Example links which will eventually die because I’m not guaranteeing this couch instance will stay up forever:
The hg repo is here. I tried to code the JS against the 1.5 standard and generally be cross-browser compatible, but I know at least Konqueror seems to get upset when it comes time to post (modified) comments. I’m not sure what’s up with that.
Exciting potential taglines:
Last time I played with Review Board and bugzilla request queues things were great for me, but no one else. I had to create an account for you on the instance, add you to my script that synchronizes request queues, run the script, and then keep running the script periodically. Not to mention there wasn’t really a way to get your review out and into bugzilla. No one actually tried to use it, so they probably also didn’t notice there were caching issues related to the ever-changing definition of “HEAD” (“tip”). It sucked, and when I upgraded and everything broke, no one cared, not even me.
But now it’s back and better than ever:
Here is a (fake) example of the “pretty” review output that is possible if you tell people about your reviewboard review (see it live here). Although it says Bienvenu, it’s just me pretending to be him because his review queue is more interesting than mine. The comments are accordingly mine.
Now, what does the export look like (see it live here)?
on file: mailnews/base/src/nsMessenger.cpp line 635 > // if we have a mailbox:// url that points to an .eml file, we have to read > // the file size as well what a pretty comment on file: mailnews/base/src/nsMessenger.cpp line 642 > NS_ENSURE_SUCCESS(rv, rv); please rename rv ARRRRRR-VEEEEEE
Yeah, it looks like that.
A quick feature summary that explains why this is better than just looking at diffs in bugzilla:
Useful links:
Caveat usor:
Props:
BugXhibit, the Bugzilla search results viewer made with the SIMILE Exhibit widget, is now more fancy, and now addresses another one of my use cases. I frequently find myself wanting to point someone at a bug, or go back to a bug that I know passed through my bugmail recently, and have trouble finding it. So now BugXhibit can do easy searches based on reporter/assignee/cc/commenter with time ranges.
Examples by way of live links this time (noting that the default time interval is 7 days). Uh, and if it gives you an error for reasons I don’t fully understand if you open it in a new tab (in the background) from here, just hitting enter in the address bar should fix it. I’m going to lazyweb that problem for now.
Other changes:
Other notes:
The hg repo is here, as always.
The above screenshot is of a normal search query on DevMo for “customize toolbar”. I see 2.5 results, and I honestly have no interest in the first item at all. (It’s a page that only advanced DevMo authors would care about, for those who refuse to squint or click on images to see bigger versions of images.)
The above screenshot is of the same query using DevMoXhibit. You will note you can see more things, and the first result from the other page is completely elided because we filter by default so that only “Real” result pages are shown. (In general, I am not looking for talk pages or user pages or meta-pages.)
But enough about my interpretations of pictures, why don’t you:
Neat things we do that may not be immediately obvious:
Implementation notes:
The hg repo is here.