The Performance Beacon

The web performance, analytics, and optimization blog

No, thanks, I really did want prototype

In CloudTest, we’ve built an error capture and reporting process similar to what you see in Apple’s crash reporting.  If we detect an error, either on the server or in the client’s web browser, we’ll show an error dialog which allows the user to supply additional information and submit the error to SOASTA so our engineers can fix the problem.  It’s been immensely helpful to find and diagnose bugs.

As an engineer, this can be a blessing and a curse.  It means you’ll get lots of bug reports very quickly if you’ve got a problem.  In Java land, on the server, this usually is great.  We can capture detailed stack traces and attach the application server’s log files to the bug.  This drastically reduces the time to research and reproduce bugs.

In JavaScript land, on the client’s web browser, results vary.  Currently the best method for getting client-side errors is to use window.onerror.  It’s not perfect.  It doesn’t catch every error, and many times catches harmless errors you’re not interested in.  Additionally, the information that’s captured isn’t always as rich as server-side (Java) errors, and many times doesn’t contain a stack trace at all, rather just a single line of text.  This generally leads to bug reports that cannot be reproduced, thus making a fix very difficult or impossible.

Recently, we’ve noticed an increase in a certain “class” of errors from JavaScript.  The code base for CloudTest was started in 2006.  At that time, jQuery didn’t exist yet, the initial release came a few months after.  However, Prototype JS did, and was judged to be the best option for our needs.  We folded it in and have never looked back.  If we were to make that decision again, we’d probably go with jQuery just because of the widespread adoption and community support.  Still, to this day, Prototype JS is a great framework, and I’m always happy to use it.  I’ve never found anything in the base jQuery library that Prototype doesn’t handle in an albeit different manner, but equally adept.

Now – getting back to that comment about widespread adoption.  It would seem that many browser extension developers believe that jQuery really should be the one-and-only JS framework to exist on today’s modern web.  So much so, that they inject jQuery into the current DOM, without using the noConflict() method!  For many sites, this isn’t a problem.  However, if your web browser is on a website that already defines any function in the global namespace that jQuery also defines, jQuery will overwrite it.  This isn’t a specific problem with jQuery, rather a problem with how browser extensions are written in some cases.  For our particular problem, if a website (such as CloudTest) uses a JavaScript library (such as Prototype JS), which defines a function in the global namespace (such as the $ function), and a browser extension injects some JavaScript (such as jQuery) which defines the same function into the global namespace, your code will be overwritten.

Obviously this is a very powerful tool, and is the crux of extensions like Greasemonkey.  However, when wielded incorrectly, it can cause disastrous effects.  Imagine a website that has code written specifically for Prototype JS, with lots of calls to the $ function.  In Prototype JS this is a shortcut for document.getElementById().  In jQuery, the $ function returns the global jQuery object.  If, for instance, you want to set an attribute on a DIV with id “hello”, you might write:
$(“hello”).setAttribute(“message”, “goodbye”);

When this code executes after Prototype has been overwritten with jQuery, instead you’ll receive an error similar to:
Object [object Object] has no method ‘setAttribute’

This proved to be one of those non-reproducible head-scratchers for quite some time, until we could view this on a customer’s browser first-hand.  5 minutes of debugging showed a BitTorrent browser extension was spamming the entire global DOM with jQuery, after the DOM had loaded.

So – what to do?  We aren’t going to go back and rewrite hundreds of thousands of lines of code to retrofit for jQuery, just so a customer can use a BitTorrent extension with CloudTest.

Instead, it’s a simple matter to detect this and give a friendly message suggesting the user disable browser extensions.  In our error capture process (from window.onerror), there’s now a simple test:

if (typeof jQuery != “undefined” && jQuery == $)
// show a friendly message and return.

Hopefully this will lend to less time scratching heads and more time writing cool new features.

SOASTA Marketing

About the Author

SOASTA Marketing

Follow @CloudTest