IE9 User Agent in HTTP Requests vs navigator.userAgent

Written by Ryan on June 6th, 2012

While tring to figure out why file uploads weren’t working in IE9 on SourceAudio, I discovered an interesting quirk: IE9′s user agent as reported by navigator.userAgent isn’t necessarily the same as the user agent that it sends in for http requests.

Apparently this is intended and understood behavior but it was the first I’d heard of it.

To summarize, MS found that as programs and add-ons added “feature tokens” to your user agent string, the length of the string would become so long that some servers would throw a fit. To prevent the issue, IE9 stopped adding these feature tokens when they send the user agent to the server, so instead of sending

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E)

You just send in

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)

However when accessing the user agent through javascript, you get the whole thing.

So why does that matter?

SourceAudio uses your user agent as salt for encryption so the server needs a consistent user agent. Ordinarily, the browser would always send the same one so it doesn’t matter but when doing flash uploads, flash doesn’t use the same user agent (or cookies, annoyingly) as your browser normally does so we have to send in the user agent manually using navigator.userAgent and suddenly it’s different.

As a solution, rather than sending in the user agent directly, we can just use

navigator.userAgent.replace(/(Trident\/[0-9.]+);.*/, '$1)')

Which strips out all the feature tokens after the Trident version number, returning the user agent to the format that’s used for http requests.

Of course, that’s only a good idea for IE9 because previous versions sent in all the feature tokens, servers be damned, so you’ll want to either browser detect before applying the replace or try something like

navigator.userAgent.replace(/(Trident\/[5-9]\.[0-9.]+);.*/, '$1)')

IE9 is just Trident/5.0 but I threw in the 5-9 assuming future versions of IE will exhibit the same behavior.

I haven’t tried this in IE10. Can anyone report on its user agent behavior?

 

Javascript prototype functions and performance

Written by Ryan on September 2nd, 2011

Apparently I decided to spend my Friday night profiling javascript and I figured I might as well share a couple performance differences I discovered where I wasn’t expecting them.

First off, it’s apparently faster to instantiate objects by extending their prototype than it is to put their methods inside the function’s brackets. I got this idea from this stackoverflow question and am going to show a variation of the test proposed by Andrew:

var X,Y, x,y,z, i, intNow;

X = function() {};
X.prototype.message = function(s) { var mymessage = s + "";}
X.prototype.addition = function(i,j) { return (i *2 + j * 2) / 2; }

Y = function() {
    this.message = function(s) { var mymessage = s + "";}
    this.addition = function(i,j) { return (i *2 + j * 2) / 2; }
};

function Z() {
    this.message = function(s) { var mymessage = s + "";}
    this.addition = function(i,j) { return (i *2 + j * 2) / 2; }
};

intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    x = new X();
}
console.log((new Date()).getTime() - intNow); 
// Chrome=1089ms; IE9=1494ms; FF=1842ms; Safari=414ms

intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    y = new Y();
}
console.log((new Date()).getTime() - intNow); 
// Chrome=1270ms; IE9=1721ms; FF=2266ms; Safari=896ms

intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    z = new Z();
}
console.log((new Date()).getTime() - intNow); 
// Chrome=1292ms; IE9=1713ms; FF=2257ms; Safari=885ms

You can see from the times after the console logs that using .prototype is faster across the board. It's not even really close in some browsers. I've always liked putting object functions inside the objects' brackets because I think it improves readability but the results are pretty clear - you pay a solid performance penalty for that organization.

Also, unrelatedly, Safari smokes everybody at this test.

That made me curious about whether or not it would also be faster to call functions assigned to an object's prototype than it would be to call the same function declared globally. I modified the previous test and ran:

var i, intNow;

function test() {
	return this + '-test';
};

String.prototype.test = function() {
	return this + '-test';
};
var t = 'test';

intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    test(t);
}

console.log((new Date()).getTime() - intNow); 
// Chrome=2504ms; IE9=2160ms; FF=5623ms; Safari=944ms

intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    t.test();
}

console.log((new Date()).getTime() - intNow); 
// Chrome=2329ms; IE9=1286ms; FF=1955ms; Safari=486ms

It appears to be consistently faster to attach a function to an object and call it than it is to call the function globally. Does that mean you should always attach functions to objects? Perhaps. I think there's a fair scope related argument for doing that anyway but the performance benefit was a lot higher than I expected.

Now if you'll excuse me, I need to go change some code on SourceAudio. Apparently I've been doing this all wrong.

Note: Don't judge Chrome vs the other browsers in these tests. It's my workhorse browser and I've got seven tabs open while the others were all fresh boots.

 

Richard Dawkins: Faith School Menace

Written by Ryan on August 23rd, 2010

You should really watch Richard Dawkins: Faith School Menace:

I found the part with the teather and students in the Muslim faith school to be particularly frustrating. How can you reasonably expect pupils to make an educated decision about which viewpoint is correct when their instructor clearly has her own, anti-science belief and can’t even answer common questions about evolutionary theory? All the while you’re teaching them in another class that all the information from the Koran is absolutely correct. When conflicts arise, are they going to follow the complex theory that was just taught half-assed by someone who doesn’t believe a word of it or are they going to go with the view that was taught with much conviction and fervor in a different class (which, btw, you’ll go to hell if you don’t choose).

Is that really an atmosphere of ideological equality? Nevermind, as he talks about later, that kids (and even adults) are more inclined to believe purpose driven explanations anyway because it’s the way we’re wired. If you want to teach evolution and you want children to really get it, you need someone teaching it who really understands it themselves and wants to convince their students. An Islamic shill is not going to do that. They can claim they’re giving children choice but without proper education on both viewpoints and without giving them proper tools with which to make choices (an education founded in critical thinking instead of indoctrination), it’s not really a choice at all.

And then the guy invokes the “just a theory” and you know that was dropped in science class without any explanation of the difference in definitions between scientific and common usage of the term.

PS – Thanks for not letting US viewers watch it on your website, Channel 4. I was perfectly happy to support you and contribute to your advertising revenue by watching your version but I guess that’s not happening.

 

Google TV – Gaming Console?

Written by Ryan on May 20th, 2010

Google today announced Google TV, which, while doing a number of other interesting things, allows you to easily run Android apps on your TV. There are plenty of games in the Android Market. If you had a good controller, I’m not sure how that experience would be very different from playing something on a traditional gaming console.

Sure, there are other systems you’d probably want in place – friends, achievements, etc. – but you already have your contacts built into the phone and there are third party achievement systems even now (though something a little more ubiquitous would be nice). Graphics are going to take a hit but Google could easily remedy that by throwing in some specialized hardware and you could certainly play less graphically intensive games in the meantime.

There are obviously a number of hurdles still but they’re talking about getting a device with a content delivery network and pre-built library of games and attaching it your TV. That’s going a lot of the way. And if you build a game for that, it’s also going to show up on one of the biggest smartphone install bases – that’s not a bad deal.

 

The Problem with document.location.hash

Written by Ryan on May 10th, 2010

SourceAudio, like a lot of ajax heavy applications, uses the hash to store state information. For example, when you search, you might end up on a url like http://www.sourceaudio.com/#explorer?s=search+terms&pg=1

The “page” is the “explorer” and the parameters are after the question mark. There are a number of ways you can format your hash but using the standard url format has been a pretty good solution for us. At least until today.

In the code, when we need to get that hash information, we would use document.location.hash but I realized today that Firefox has a problem when you start having ampersands in values. Naturally, you’d encode them with escape() to end up with something like #explorer?s=bump%20%26%20grind&pg=1 (from “bump & grind”) but when you try to retrieve that with document.location.hash, Firefox automatically un-escapes the hex codes.

Try it out. Go to firebug and run

document.location.hash = 'a%20%26%20b';
console.log(document.location.hash);

You’d hope to see ‘a%20%26%20b’ but instead, you get ‘a & b’.
Just ran Chrome out of curiosity. It returns ‘a%20%26%20b’, as does IE8.

I guess there are applications where Firefox’s behavior would be useful but it’s a real problem when the ampersand is a special character in your syntax. There’s no way to tell the difference between an & and a %26. So if you’re trying to parse ‘s=bump%20%26%20grind&pg=1′ so ‘s’ is ‘bump & grind’ and ‘pg’ is ’1′, you instead get ‘s=bump & grind&pg=1′ which parses to ‘s’ = ‘bump ‘, ‘ grind’ = ”, and ‘pg’ = ’1′. It’s no good.

So, what’s the answer? Apparently it’s not to use document.location.hash at all. Consistently across browsers, document.location.href contains the full path, without any un-escaping. So if you want the hash, all you have to do is

var hash = document.location.href.replace(/^[^#]+#/, '');

Just don’t trust document.location.hash
Or the Firefox developers, it seems.

 

Knight’s Tour

Written by Ryan on December 22nd, 2009

I wrote a little program to calculate the “winning” boards of the Knight’s Tour problem after my grandfather brought it up and noticed some C# array performance stats while I was at it. I ended up using lots of .Clone() operations on the arrays and just some basic index accessing.

On the first try, I wrote it with a multidimensional int[,] array and it took 48.5s to run 10M iterations. After reading that you should really flatten multidimensional arrays, and changing the code to just use int[], it only took 10.2s to run otherwise identical code. That’s pretty huge.

Out of curiosity, I switched it again to use ArrayList objects and knocked it even further down to 6.4s.

So, multidimensional arrays, slow! ArrayLists, fast! Is anything else better?

 

Asynchronous PHP with wget

Written by Ryan on September 23rd, 2009

I have a site where there’s a cache of a bunch of tracks (music site) and there are instances when a user does something and I need to rebuild that cache. The rebuilding takes a good five or ten seconds and will only become slower as we get more tracks so having the user wait while I do that is no longer feasible. I have a script that does a periodic rebuild (every fifteen min) but I’ve found that users don’t understand that their changes were accepted when they don’t show up for several minutes. It has to rebuild on demand but asynchronously. That way you get their changes applied pretty quickly but don’t have to wait on it.

I tried googling for asynchronous php solutions but couldn’t get anything to actually work. Brent accepted this answer but I had no luck with that. It would make the call but it wouldn’t do it asynchronously. I always had to wait for the thing to finish.

Some people suggested using the command line to run php and make the call that way but I couldn’t do that because of some architectural crap. I came up with this beauty though:
exec('wget -O /dev/null -o /dev/null -qb --no-cache --post-data=foo=bar http://theurl.com/whatever.php');
and it works perfectly. You can call a script on any server (not just your own like with php) and it runs in the background and discards the output quietly. Just one line and forget it.

Naturally, this only works on Linux servers with wget installed. Sorry windows folks

 

jQuery document.body is null error

Written by Ryan on September 23rd, 2009

I had a little “fun” with jQuery where it was giving me a “document.body is null” error…or sometimes it wouldn’t say anything but would just quietly fail. After some investigating, I made an interesting discovery about jQuery’s data() function.

I was chaining calls to data, like
myObject.data('foo', foo).data('bar', bar);

which generally works fine but apparently if foo is undefined, an error is thrown and the whole thing dies. If bar is undefined, it’s no problem, but something about trying to call data() with an undefined value breaks jQuery’s ability to make any further chained calls to that element.

The easiest solution I found is just dropping in a little
if (!foo) foo = null;
myObject.data('foo', foo).data('bar', bar);

And then everything works!

 

WP Post Thumbnail IE Error

Written by Ryan on September 14th, 2009

I just finished rebuilding Fighters.com and used WordPress for the whole thing which was a new experience for me. I hadn’t done any real work customizing before and it’s a really interesting design. The documentation isn’t quite what I’d like it to be but I was able to figure out everything eventually.

One particularly vexing issue was with the plug-in WP Post Thumbnail which gives you a ridiculously convenient way to add thumbnails to each post (Note that I intentionally linked to the older version because I like the interface a lot better). When you add/edit a post, you can upload an image right there and crop/scale it to different, configurable dimensions before adding it to the post. It was perfect for what I needed it for except the admin didn’t work in IE.

When IE tries to load a post edit page with WP Post Thumbnail enabled, it gives the very helpful:

Webpage error details
User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322)
Timestamp: Mon, 14 Sep 2009 23:23:31 UTC
Message: Invalid argument.
Line: 12
Char: 12949
Code: 0
URI: http://www.fighters.com/wp-admin/load-scripts.php?c=1&load=jquery,utils,editor,quicktags&ver=33f9a1c72519a04d2fd7dd5a26c9ec8d

If you track down character 12,949 on line 12, it’s jQuery code that I believe is several functions inside an attr call. However what the exact problem is at that point eludes me. It’s trying to set variable[zIndex] = null; and I fail to see an invalid argument in that.

Now IE8 actually has a pretty nice js debugger but apparently it can’t stack trace code that was dynamically added to the page using jQuery.load, which of course this was, so I couldn’t see what has actually making the attr call. All I could figure out to do was put alert() messages all over all the dynamically loaded files and I eventually tracked it the offending call to this line in plugins/wp-post-thumbnail/wppt_repository.php:

jQuery('#bigimg').imgAreaSelect({hide:true});
I soon discovered that any call to imgAreaSelect threw the same error.

Looking for the source to that function, I found it within the imgAreaSelect Library which came with WP Post Thumbnail. The version that came with WP Post Thumbnail is v0.5.1 but if you go the imgAreaSelect site, you can get v0.9.1 (as of this writing).

Download the new imgAreaSelect code, unzip it, drop the .min.js file in your plugins/wp-post-thumbnail/js folder, add the css of your choice to plugins/wp-post-thumbnail/css/wppt-admin.css (if you choose the animated one, copy the images into your css folder too), and lastly, update plugins/wp-post-thumbnail/wppt.php’s wppt_js() function to have the correct path to your new imgAreaSelect js file. Presto! WP Post Thumbnail will be fully functional in IE.

At least that fixed it for me :P

 

Javascript Copy to Clipboard Follow-Up

Written by Ryan on July 21st, 2009

While testing a site using the previously recommended ZeroClipboard in IE6, I noticed it was throwing occassional errors I believe are related to IE saying Flash objects are available before they actually are, or before they’ve registered their ExternalInterface functions with Javascript. Anyway, I tried changing the ZeroClipboard js file to make sure all Flash functions were registered before trying to use them which got rid of the errors, but IE6 still didn’t actually copy anything to the clipboard when hitting the button.

Rather than try to fix that, I took advantage of IE’s built in clipboard manipulation object, window.clipboardData and ended up with this:

if ($.browser.msie) {
  $('#clickme').click(function() { 
    if (window.clipboardData.setData('Text', $(this).data('copyText'))) {
      postCopyFunction(); // only happens if user allows copy
    }
  }).data('copyText', 'text to copy');
} else {
  var clip = new ZeroClipboard.Client();
  clip.setHandCursor(true);
  clip.addEventListener('complete', postCopyFunction);
  clip.setText(url);
  clip.glue('clickme');
}

Works about the same except in IE you get a confirmation about whether you want to allow access to the clipboard, which is acceptable. Arguably works better in IE now that it does in other browsers because you’re not loading a Flash widget.

Just a heads up if you want to use ZeroClipboard for a project.