TinyUrl Trouble: Greasemonkey drops the location header in GM_xmlhttpRequest

I get a lot of ideas. Most of them wander aimlessly in my head until they become obsolete, but once in a while I’ll get an idea that seems useful and simple enough to do in my free time.

If you’ve used Twitter, you’ve seen the myriad of url shortening services like TinyUrl and Bit.ly. Url shortening services are a kludge and they break one useful, built-in feature of the web, which is the ability to know where you’re going when you click a link.

So I thought, this is something that I could fix in an hour or so with a Greasemonkey script. If you have no idea what I’m talking about, Greasemonkey is a Firefox Plugin that runs in your browser and lets you run your own Javascript on pages you load. Greasemonkey comes with a handy-dandy AJAX function called GM_xmlhttpRequest.

I figured all I have to do is grab all the anchors on the page, see if they match a list of shortener urls, do an xmlhttpRequest for each one and grab the final location (after the service finishes with it’s redirecting) from the headers.

Something along these lines:

function getTargetUrl(short_url) {

  GM_log('Getting '+short_url);

  GM_xmlhttpRequest({
      method: 'GET',
      url: short_url,
      headers: {
          'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
          'Accept': 'text/html'
      },
      onload: function(responseDetails) {
          GM_log('Done.  Status ' + responseDetails.status +
                ' Text ' + responseDetails.statusText + '\n\n' +
                ' Headers:\n' + responseDetails.responseHeaders);
      }
  });
}

As you can see, it should just be a matter of plucking the location header from responseDetails.responseHeaders.

But every time I fetched a url, even though Firefox theoretically handles GET redirects correctly, I never found a location in the headers returned.

If you can’t do an AJAX call, you can do things the old-fashioned way with hidden iframes.

  var the_iframe = document.createElement('iframe');
  the_iframe.setAttribute('style', 'height:10px;width:10px;');
  the_iframe.setAttribute('src', short_url);
  document.body.appendChild(the_iframe);

The problem there is that although I can easily create the iframe, it’s hard to get the final location. If I get the value of the src attribute, it’s still set to whatever I set it to in the first place. I tried using the location of the contentWindow:

GM_log('Current href: '+
      document.getElementById('embiggen'+num).contentWindow.location.href);

…but that gives me “about:blank” since my GM script runs before the iframes are loaded.

I played around a bit adding and event listener to the iframe but got access errors. Then it got late so I gave up. It turns out lots of other people have already had the same idea.

Here’s a short url expanding GM script that looks promising, if you’re here to get actual help and not just listen to my whining.

If you’re also annoyed by tinyurls or if you’re a fellow Javascripter, and you have found a glaring error in my plan, let me know in the comments below.