7 November 2009 - 23:32Chrome extension: edit Gmail textarea in an external editor

A month or two ago, I switched to using Chrome daily builds (on Debian) as my primary browser. As with most people switching to Chrome, I love the stability and speed, but there are a few extensions from Firefox I sorely miss. A major one is mozex, which allows you to edit textareas in an external editor (It’s All Text also does this). I used this for editing Gmail. I used Gnus as my primary mail and news client for years, and one of the big sticking points to switching to webmail was the anemic textarea editing interface. I’m just used to all of the trappings of Emacs — dict-mode, fill-paragraph, the keybindings, remembrance agent, etc.

Anyway, I decided to see if I could whip up a quick and dirty Chrome extension to substitute for mozex. And when I say quick and dirty, I mean it — this code would make a Perl-happy sysadmin blush. But I hacked something up in a few hours, and I thought I’d put it out there since I find it useful. Maybe someone else can run with it and make it more complete like mozex. With a few one-line edits, this extension can be made to allow the editing of virtually any textarea in any external editor. I limited it to Gmail because I don’t know if there is the possibility of resource leaks or other weird side-effects from running it more extensively. Also, it’s hard-coded to use Emacs, but you could just as easily use Gvim (have it spawn an xterm with an editor or use emacs-client or whatever).

I’ve put the extension source files up here:
http://www.thegibson.org/blog/files/emacs_chrome

Edit: see http://www.thegibson.org/blog/files/emacs_chrome/sync_ver — the original only works right on platforms like Linux due to a serendipitous interaction (see comments).
Edit2: see http://www.thegibson.org/blog/files/emacs_chrome/ba_sync_ver/ — this version uses a Chrome “browser action” rather than a “page action.” The “browser action” extension type is better suited for this. Note that some of the text below is not longer applicable to this new and simpler version, so see comments. I’ll try to make a new post soon with the updated info.

The extension comes with a Python script (you’ll need Python 2.6 to run it). The pycl.py script is a little web server running on port 9292. Run the web server and leave it in the background. Then add the extension to Chrome (go to chrome://extensions). Once you add the extension, you can load up Gmail and either compose or reply to a plain text message (not HTML). Then use the “compose in a new window” icon to open a new window, and you’ll see an Emacs 23 icon appear in the “page action” area (the upper right-hand corner of the address bar, where the SSL lock icon goes). Clicking the icon sends the contents of the text area to the Python web server, which writes it to a temporary file (using tempfile.NamedTemporaryFile) and spawns an editor. Edit your text; when you’re done, save and exit. In the meantime, the background script will start polling the web server to see if the editor has terminated. When the editor finishes, the file contents are read and sent back to Chrome, which modifies the textarea with the new contents.

Anyway, it has several major limitations — first and foremost, it only works consistently if you compose or reply in a new window (click that little diagonal arrow icon in the upper right hand corner of the message frame). Sometimes it works on the main Gmail page, but not always. Due to the hooks it uses, if you open the new window while viewing the message, and then click reply, you’ll have to type something in the textarea or un-focus and re-focus the window before it’ll “notice” the existence of the new textarea. This is a limitation of the way the extension content script is finding the textarea in the page. Maybe the fact that it only works consistently in a new window has to do with Gmail’s crazy dynamic DOM manipulation. Or maybe — perhaps even more likely — I’m missing something obvious. I’m sure someone with more Javascript / DOM / experience could probably fix this. I’ve never used Javascript in any context before, so it was an interesting experience. I basically looked at two Chrome example extensions — 1) the RSS feed subscription and 2) the Gmail checker — and munged their code and techniques together.

Changing editors
To change editors, just edit pycl.py and modify the line:

p = subprocess.Popen(["/usr/bin/emacs", f.name])


Making it work on arbitrary textareas
If you want to change the extension to work on more than just Gmail, first edit majifest.json and change the “matches” line:

"matches": ["http://mail.google.com/*", "https://mail.google.com/*", "file://*/*"],

"matches": ["http://*/*", "https://*/*", "file://*/*"],

The second matches example works on all http and https websites.

Then, in the ta_find.js file, change the getElementsByName(‘body’); line:

  result = document.getElementsByName('body');

  result = document.getElementsByTagName('textarea');

The second will find any textarea on the page (one limitation of the current incarnation is that it will only deal with the first textarea, however).


Making it not suck
This is my first foray into in-browser Javascript programming. I’m sure there’s a lot of missing exception handling in background.html when dealing with the XmlHttpRequest objects. There might be resource leaks too. And there are corner cases where it won’t get the textarea contents to send to the external editor correct (like if you only use mouse cut and paste to change the contents around without any keypresses). Also, in the Python script, there’s not much error handling either, and using a dedicated private directory (like mozex) rather than tempfile.NamedTemporaryFile is probably advisable on shared systems. Also, it’d be great if some Javascript wizard could help figure out why it only works with Gmail when you open a new window. Ideally I would have liked to add a context menu item to all textareas, but I figured it was easier to use existing Chrome extensions as templates and do it with the “page action” mechanism.


Thoughts on Javascript
Looking at the way that the Gmail checker and the RSS feed subscription work, I see an extensive use of closures and continuation-passing style asynchronous programming. Given JavaScript’s reputation, this is definitely a lot nicer and cleaner than than what I expected. Steve Yegge has also pointed out several times that JavaScript is a nicer language than people actually give it credit for, and now I know firsthand.

19 Comments | Tags: Uncategorized

4 November 2009 - 2:10Flash and the storage hierarchy (again)

Earlier this year in a post titled “Disk is the new disk”, I ruminated a bit on the implications of the changing storage hierarchy. One of the frequently discussed changes on the horizon is the introduction of flash memory (or other solid state, non-volatile memories like phase-change memory, which I mentioned in that post) into the storage hierarchy. One question I didn’t address at the time is, “do these solid state non-volatile memories belong as external or internal memory?” That is to say, “do these belong on the memory bus or IO bus?” With flash memory, the latencies and writing semantics (i.e., individual bytes can’t be arbitrarily modified; entire blocks are erased at once) tend to naturally place flash memory devices on the IO bus as external memory. However, other technologies may be more appropriate to expose as directly CPU-addressable memory. A recent paper at SOSP ‘09 explores this in the context of Phase-Change Memory (although the techniques are applicable to any directly-addressable BPRAM — byte-addressable persistent memory): “Better I/O Through Byte-Addressable, Persistent Memory”. Unlike flash, PCM can be read and written in a byte-addressable manner, and access times are fast enough to merit putting PCM on the CPU’s memory bus.

The paper introduces BPFS, a filesystem for BPRAM which exploits various properties of directly-addressable non-volatile memory to improve performance and durability over traditional filesystems. It’s interesting how a lot of the very thorny problems of filesystem consistency associated with block-based disk interfaces are solved elegantly by applying traditional in-memory style atomic updates to data structures in BPRAM. Traditional filesystems use relatively hairy techniques like soft-updates or journaling to try to ensure that persistent data structures are updated in a way that preserves metadata consistency even if the system fails in the middle of a write. With byte-modifiable data structures, you can use atomic update techniques similar to those used in lock-free data structures (e.g. do modifications on a private copy of data and then atomically “publish” it by setting a pointer field pointing to the private copy — and make sure to put architecture-appropriate fences so reordering doesn’t bite you!*). When I started reading the paper, two classic systems paper quickly came to mind: “Lightweight Recoverable Virtual Memory” and “Free Transactions with Rio Vista”. Rio Vista owes a lot to RVM, but one of the memorable things about Rio Vista is that it uses battery-backed RAM to perform atomic and durable transactions on persistent data structures. Like BPRAM, the persistent but directly-modifiable nature of battery-backed DRAM really simplifies many aspects of the system. Naturally, when I got to the end of the paper, I found that they cited Rio Vista in their related work. Anyway, it’s good to see that one of the co-authors is a former Georgia Tech classmate, Derrick Coetzee.

* One of the most interesting parts of the paper is their “epoch” mechanism — while fences work fine for volatile data structures, they don’t provide strong enough semantics when you have persistent memory. Memory fences aren’t strong enough because they just affect a CPU’s view of memory, not the actual contents of DRAM. A CPU’s view of memory is basically DRAM plus “diffs” of more recent data at various caching levels. With BPRAM, if the power goes out, the diffs in cache disappear and then the persistent data left may not be consistent by itself. You have to make stronger guarantees about when persistent data gets written to maintain proper stored data structure consistency.

Linux and flash
Although the SOSP paper is recent and on my research radar, it is not what prompted this post. The original impetus came from my recent viewing of the Linuxcon 2009 roundtable discussion. This roundtable gained some Slashdot notoriety because Linus made a comment about the kernel being “huge and bloated,” due to its expanding feature set and icache footprint. During the Q&A session, someone asked a question regarding flash RAM. The question was predicated on the assumption that flash will transition to directly addressable (internal) memory and was asking whether, since flash cells have limited lifetimes, would Linux eventually integrate code to deal with directly accessible memory failing. Ted Ts’o sort of dismissed the question and said that he believed that the right place to address failure properties are in the hardware (and he didn’t necessarily agree that flash would move to directly addressable memory). Currently, flash exposed with a “disk drive” interface — i.e., flash that looks like a fast hard drive — handles failure and wear leveling underneath the storage interface.

This reminded me, however, that there is another class of flash support in Linux that is commonly misunderstood. Linux has a MTD (Memory Technology Devices) subsystem which supports “bare flash” devices. These devices are more common on embedded systems, and basically “bare flash” is exposed flash memory that doesn’t look/act like a standard hard drive. The software above gets to access the real flash blocks and has to handle wear leveling, dealing with bad blocks and also dealing with write/erase semantics (things that the firmware would do in a hard drive-like flash disk). MTD devices don’t act like block devices and actually expose three operations: read, write, and erase. There’s a whole class of Linux filesystems built to run on top of these “bare flash” devices: YAFFS, JFFS2, LogFS, and UBIFS, and they’ve also factored out some of the common functionality used in a lot of these filesystems into a separate UBI (Unsorted Block Images) layer. But I see a lot of misunderstanding of the point of these filesystems. Many casual Linux users or observers think that these filesystems are flash-optimized regular filesystems to be used on top of hard-drive like flash devices (or CF/SD/etc.). Anyway, I see this mistake made a lot on forums and such so it came to mind after the Linux roundtable question.

No Comments | Tags: Linux, Research Content