Less Talk, More Do Christopher Finke is a software engineer. He is available for birthday parties and bar mitzvahs.

Posts tagged with 'Browser Add-ons'

Making Add-on/User Communication Less Annoying

Saturday, February 27th, 2010

When a new user downloads TwitterBar, there are a number of things I want them to know or questions I want to ask them. So what is the best method to communicate with an add-on user?

The solution I've been using for a while is to pop up a dialog like this:

There are several problems with this approach, all of which I decided to ignore when I implemented it:

  • It steals the user's focus.
  • It's annoying.
  • The user might click cancel without reading it just to get rid of it.
  • It's annoying.
  • The user might immediately (but accidentally) click elsewhere, hiding the dialog behind another window, never to be seen again.
  • It's annoying.
  • It's extra code and work to pop up a special dialog like this.
  • It's annoying.

Back when there was only one dialog, I decided that these were acceptable faults. However, since then, I've come up with a few more questions I want to ask users, so now instead of one annoying dialog, there are three or four annoying dialogs - a new one appearing each time you restart Firefox.

Predictably (or so it should have been), users don't like to be assaulted with new dialogs each time they start their browser. Most likely, they're starting their browser for some purpose other than using my add-on, so my add-on shouldn't steal their attention. As one user so elegantly put it,

"I really love the TwitterBar, but after the most recent TwitterBar update, I noticed I kept getting these annoying as hell pop-ups from TwitterBar about TwitterBar. After the third one (while I was in the middle of doing something and became distracted with this pop-up dialog box TwitterBar tip of the day), I uninstalled it. If you want to keep your clients, don't constantly tap them on the shoulder."

I had already been working on redesigning these add-on/user interactions when I got that email, so the user's message reinforced what I had suspected: I was alienating my userbase.

Here's the new scheme I've settled on for now:

It's a notification bar, much like the one that appears when Firefox blocks a popup. It has these positive qualities:

  • It doesn't steal focus or interrupt the user.
  • It's not in-your-face, so it's less likely (I assume) to be dismissed without thought.
  • It can't be lost behind another window.
  • The amount of code to implement it is less, and it's more in tune with the browser interface.
  • It's not as annoying.

I'd love your feedback on this change. Is it enough? Should I stop bothering users altogether and just let them discover their way around the add-on? I'm open to all ideas.

(If you'd like to try a version of TwitterBar with this new notification method, you can download it here. Although, if you've already seen the old dialog-style version of these notifications, you won't see the new-style ones anyway.)

TwitterBar 2.9 Available: Post to Multiple Twitter Accounts

Wednesday, February 24th, 2010

Version 2.9 of TwitterBar for Firefox was made available on Mozilla Add-ons today, and it has a very cool new feature: you can now use TwitterBar with more than one Twitter account.

To post to a specific account, just type your message like this:

I am posting to my other account. --@other_account --post

If you haven't yet authorized TwitterBar for @other_account, you'll be walked through the authorization process.

If you've authorized more than one account, and you don't specify which account you want to post to, you'll be given a list of choices:

You can manage your accounts from the TwitterBar options (just type "--options").

Finally, to authorize a new account without posting to it, just type "--account" in the URL bar.

To install this new version of TwitterBar, download it from Mozilla Add-ons.

The next obvious step is the ability to post to multiple accounts simultaneously, and the next version of TwitterBar will offer than feature. If you'd like to beta-test that update, e-mail me and let me know.

Uploading form data and files with JavaScript (Mozilla)

Saturday, January 30th, 2010

One problem I stumble across occasionally in writing Firefox extensions is properly uploading form data that includes a file - that is, assembling the POST request in JavaScript while still maintaining the sanctity of any file or string data. You can't just do this:

var request = "--boundary\r\n some text\r\n--boundary" + fileBytes + "\r\n--boundary--";

I had to spend a bit of time getting this just right in order to allow ScribeFire to upload media to Posterous, so I'm posting below the final solution at which I arrived; it was cobbled together from a dozen different examples I found around the Web (none of them solving the full problem), then lovingly massaged into the elegant function you see before you. With this function, you can pass in an array of fields and files, and the request will be crafted and returned to you, ready for upload.

Instructions for use are in the comment block at the top of the function.

function createPostRequest(args) {
  /**
   * Generates a POST request body for uploading.
   *
   * args is an associative array of the form fields.
   *
   * Example:
   * var args = { "field1": "abc", "field2" : "def", "fileField" :
   *              { "file": theFile, "headers" : [ "X-Fake-Header: foo" ] } };
   *
   * theFile is an nsILocalFile; the headers param for the file field is optional.
   *
   * This function returns an array like this:
   * { "requestBody" : uploadStream, "boundary" : BOUNDARY }
   *
   * To upload:
   *
   * var postRequest = createPostRequest(args);
   * var req = new XMLHttpRequest();
   * req.open("POST", ...);
   * req.setRequestHeader("Content-Type","multipart/form-data; boundary="+postRequest.boundary);
   * req.setRequestHeader("Content-Length", (postRequest.requestBody.available()));
   * req.send(postRequest.requestBody);
   */

  function stringToStream(str) {
    function encodeToUtf8(oStr) {
      var utfStr = oStr;
      var uConv = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
      uConv.charset = "UTF-8";
      utfStr = uConv.ConvertFromUnicode(oStr);

      return utfStr;
    }

    str = encodeToUtf8(str);

    var stream = Components.classes["@mozilla.org/io/string-input-stream;1"]
                   .createInstance(Components.interfaces.nsIStringInputStream);
    stream.setData(str, str.length);

    return stream;
  }

  function fileToStream(file) {
    var fpLocal  = Components.classes['@mozilla.org/file/local;1']
                     .createInstance(Components.interfaces.nsILocalFile);
    fpLocal.initWithFile(file);

    var finStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                      .createInstance(Components.interfaces.nsIFileInputStream);
    finStream.init(fpLocal, 1, 0, false);

    var bufStream = Components.classes["@mozilla.org/network/buffered-input-stream;1"]
                      .createInstance(Components.interfaces.nsIBufferedInputStream);
    bufStream.init(finStream, 9000000);

    return bufStream;
  }

  var mimeSvc = Components.classes["@mozilla.org/mime;1"]
                  .getService(Components.interfaces.nsIMIMEService);
  const BOUNDARY = "---------------------------32191240128944"; 

  var streams = [];

  for (var i in args) {
    var buffer = "--" + BOUNDARY + "\r\n";
    buffer += "Content-Disposition: form-data; name=\"" + i + "\"";
    streams.push(stringToStream(buffer));

    if (typeof args[i] == "object") {
      buffer = "; filename=\"" + args[i].file.leafName + "\"";

      if ("headers" in args[i]) {
        if (args[i].headers.length > 0) {
          for (var q = 0; q < args[i].headers.length; q++){
            buffer += "\r\n" + args[i].headers[q];
          }
        }
      }

      var theMimeType = mimeSvc.getTypeFromFile(args[i].file);

      buffer += "\r\nContent-Type: " + theMimeType;
      buffer += "\r\n\r\n";

      streams.push(stringToStream(buffer));

      streams.push(fileToStream(args[i].file));
    }
    else {
      buffer = "\r\n\r\n";
      buffer += args[i];
      buffer += "\r\n";
      streams.push(stringToStream(buffer));
    }
  }

  var buffer = "--" + BOUNDARY + "--\r\n";
  streams.push(stringToStream(buffer));

  var uploadStream = Components.classes["@mozilla.org/io/multiplex-input-stream;1"]
                       .createInstance(Components.interfaces.nsIMultiplexInputStream);

  for (var i = 0; i < streams.length; i++) {
    uploadStream.appendStream(streams[i]);
  }

  return { "requestBody" : uploadStream, "boundary": BOUNDARY };
}

Extending the Web, not just the browser, with Twitter Filter

Thursday, December 17th, 2009

Note: If you don't use or care about Twitter or browser extensions or the Web in general, you can safely skip the rest of this post.

One of the things that annoys me about Twitter is the constant "check-ins" from location apps like foursquare and Gowalla. Since I don't use these apps, I don't especially care that you're at the Starbucks in Dallas or that you're the mayor of your bus stop.

Foursquare checkins on Twitter

To solve this problem, I wrote a browser add-on called Twitter Filter. It adds a little "X" next to the source of each Twitter status update; if you click that X, you will never see updates from that source again (at least not on the Twitter website):

8 minutes ago from foursquare (X)

The sources you've blocked are listed in Twitter's right sidebar so that you can easily unblock them:

Blocked Sources: foursquare, Gowalla

That's all there is to Twitter Filter, and it's a new type of browser add-on for me. It has options, but no options dialogs. There's no toolbar button, no browser sidebar - everything related to the add-on is controlled via the Twitter Web interface, so it's not so much an extension of the browser but rather an extension of Twitter itself. Go ahead and install it on a friend's computer and convince them that Twitter added the feature - it'll be fun! Be sure to come back and recount your grand prank in the comments below.

TwitterBar is a winner!

Monday, December 14th, 2009

(And so are you, for using it!)

It was announced today that TwitterBar is one of the ten winners of the Mozilla Mobile Add-on Challenge:

"After reviewing a multitude of submissions and much debate around many worthy contenders, the judges from the AMO and Mobile teams have chosen ten (10) of the “best” (innovative, useful, compatible) Firefox mobile add-ons. We’re very pleased to announce our winners (and thrilled to start using their add-ons soon) and award them each with a brand new Nokia N900."

I was really pleased with how the mobile version of TwitterBar turned out; I slimmed down the interface (which was already pretty slim), and I found a great Twitter bird icon that I modified to allow TwitterBar to have its own branding separate from Twitter:

I'm planning on eventually using this same icon in the Firefox version of TwitterBar too so that there's consistent branding across the Mobile, Firefox, and Chrome versions of TwitterBar. What do you think?

Lost and FireFound

Tuesday, December 8th, 2009

I've written a new extension for Firefox, and it's called FireFound:

FireFound is an add-on for Firefox and Fennec (mobile Firefox) that helps your find your computer (or mobile phone, in the case of Fennec) if it is lost or stolen. Every time your computer's location changes, FireFound sends a secure message to a central server with its current location. You can then log into the server and see your computer's current location.

All of the location data is encrypted, so no one can find out where your computer is without your password.

If you lose your computer, you can tell FireFound to clear your personal data (saved passwords, browsing history, etc.) if anyone starts your browser before you can retrieve it.

You can even run your own FireFound server; all of the code is open-source.

FireFound was chosen as a grand prize winner of the Extend Firefox 3.5 contest; since then, more than 6,000 people have installed FireFound, resulting in over 330,000 location updates.

Add-On-Con 2009

Monday, December 7th, 2009

Just a note that I'll be at the 2nd annual Add-On-Con this week in Mountain View, CA. Add-on Con is a gathering of browser add-on developers and representatives from the browser vendors (Mozilla, Google, and Microsoft).

I'll be participating in the opening keynote discussion, which will address the question, "Do Add-ons Need a Marketplace?" The other keynote participants will be representatives from Mozilla, Microsoft, XMarks, and FoxyProxy, so I'm obviously there to even out the low end of the curve. Anyone know any good knock-knock jokes I can use?

I'll also be sitting on a Mozilla panel as a token add-on developer, talking and answering questions about how to develop a successful add-on. (On an unrelated note, if you know how to develop a successful add-on, please contact me ASAP.)

Will you be at the conference? Let me know!

TwitterBar for Google Chrome

Friday, December 4th, 2009

If you're using Google Chrome, and you're running the developer version that allows extensions to be installed, you can now install TwitterBar for Google Chrome.

After you've installed it, just click on the Twitter icon in the toolbar bar, type your message, and press Enter to post your message to Twitter. (You'll have to authorize TwitterBar the first time you tweet, but it only takes a second.)

If your message gets too long (Twitter only allows 140 characters), the happy blue Twitter bird will turn into an angry red Twitter cardinal.

Let me know what you think; I tried to keep as much of the functionality from the Firefox version as possible.

Feed Sidebar 4.0 Released

Thursday, June 18th, 2009

Feed Sidebar 4.0 has been released for all users of any previous version of Feed Sidebar, and this version has two great improvements over version 3.1:

  1. You can now sort your feeds by three different criteria:
    Sort by Name, Date Updated, or Date Added
  2. Feeds are now updated constantly, rather than all at once. So if you have 60 feeds, and you have Feed Sidebar set to update once per hour, instead of updating all 60 feeds after an hour, Feed Sidebar will now update one feed every minute. This should fix any issues users have had with Firefox locking up while feeds are being update.

You can read more about these improvements in my previous blog posts: Feed Sidebar 3.2 Beta 3: A Gentler Feed Updater, Feed Sidebar 3.2 Beta Update, and Sort your feeds in Feed Sidebar.

To upgrade to version 4.0, you can install this update from Mozilla Add-ons, or if you already have have Feed Sidebar installed, it will be automatically downloaded for you.

TwitterBar 2.4, Now With 2% Longer Tweets

Thursday, June 11th, 2009

TwitterBar 2.4 was released this morning, and the main change is that it now integrates the URL shortening service tweak.tk. The URLs that .tk generates are the shortest you can get, weighing in at 15 characters; TwitterBar's previous URL shortener, is.gd, generated 18-character URLs.

What does this mean? Well, now when you post a URL to Twitter with TwitterBar, you have 125 characters for your message instead of 122. So feel free to toss an extra LOL or BRB in there; you've got plenty of room.