Blog


.nodeName Case Sensitivity

When working with the DOM .nodeName property there are two hard-and-fast rules that most people abide by:

  1. The node names of HTML elements are always uppercase, even if they're explicitly created using lowercase characters. <html> will result in a .nodeName === "HTML" (see the HTML 5 draft).
  2. The node names of XML elements are always in the original case, as specified when they're created. <data> will result in a .nodeName === "data", <DATA> will result in a .nodeName === "DATA".

Knowing these rules can be useful because it allows you to optimize your code. If you know that you're in an HTML document you can avoid having to upper/lowercase your .nodeName checks and you can just always assume that you're dealing with a .nodeName that's uppercase. This results in faster selectors for Internet Explorer and other minor optimizations.

However recently I've been running across two cases that've been especially problematic and have bucked the trend.

Importing Nodes from XML

The first is for browsers that support the adoptNode/importNode DOM methods. These methods allow you to move (or clone) a node from one DOM document to another. In this way you can move an XML node from an XML document and insert it into an HTML document. Normally this shouldn't matter much but, as it turns out, the original .nodeName case sensitivity is preserved from the original XML-ness of the node.

Thus if you have a lowercase XML element (<data>) and you use adoptNode or importNode to bring it into your HTML document the result will be .nodeName === "data" -- which completely bucks the trend for "all HTML element's node names are always uppercase." I consider this to be a bug, considering that the DOM element is now in an HTML document, not in an XML document, and should behave as such.

Unknown HTML 5 Elements

The second bit of weirdness comes from people attempting to use the new elements from HTML 5 in browsers that don't support it. Most browsers behave perfectly well when using some of the new HTML 5 elements (in that they don't freak out and support some level of styling). For Internet Explorer you must use the HTML 5 Shim technique - this will give unknown HTML 5 elements the ability to be styled and hold contents (such as a <section> element).

However there is an additional gotcha: When Internet Explorer encounters an element that it doesn't recognize it leaves the .nodeName in its original case. Thus if you have a <section> element in your HTML page the result will be .nodeName === "section" -- which directly contradicts the normal case sensitivity of the .nodeName property in HTML documents.

To try and understand all of this I made a bunch of test cases using a number of doctypes and document styles.

The important part of the test page is quite simple:

<!DOCTYPE html>
<html>
<head>
  <title>Testing nodeName</title>
</head>
<body>
  <div id="test">
    <div></div><DIV></DIV>
    <section></section><SECTION></SECTION>
  </div>
</body>
</html>

and the test cases are as follows:

HTML

Accesses the HTML elements that were originally included the page (should be case insensitive).

runTest("HTML", function(){
  return document.getElementById("test").childNodes;
});

HTML createElement

Creates new DOM elements using the same document as the page in which it was shipped (should be case insensitive).

runTest("HTML createElement", function(){
  return [   
    document.createElement("div"),
    document.createElement("DIV"),
    document.createElement("section"),
    document.createElement("SECTION")
  ];         
});

innerHTML

Attempts to inject the elements using .innerHTML (should be case insensitive).

runTest("innerHTML", function(){
  var test = document.getElementById("test");
  test.innerHTML = "<div></div><DIV></DIV>" +
    "<section></section><SECTION></SECTION>";
  return test.childNodes;
});

For the remaining tests I grab a simple XML document:

<?xml version="1.0" encoding="UTF-8"?>
<test>
  <div></div><DIV></DIV>
  <section></section><SECTION></SECTION>
</test>

like so:

var xhr = window.XMLHttpRequest ?
        new XMLHttpRequest() :
        new ActiveXObject("Microsoft.XMLHTTP");

xhr.open("GET", "test.xml", false);
xhr.send(null);

var xml = xhr.responseXML;

XML

Test the elements in the XML document directly (should be case sensitive).

runTest("XML", function(){
  return xml.documentElement.childNodes;
});

XML createElement

Same as the HTML createElement but done using the XML document (should be case sensitive).

runTest("XML createElement", function(){
  return [   
    xml.createElement("div"),
    xml.createElement("DIV"),
    xml.createElement("section"),
    xml.createElement("SECTION")
  ];         
});

HTML via importNode

This clones the nodes from the XML document, using importNode, and places them into the HTML document (should be case sensitive).

runTest("HTML via importNode", function(){
  var test = document.getElementById("test");
  while ( test.firstChild ) {
    test.removeChild( test.firstChild );
  }           

  var nodes = xml.documentElement.childNodes, node;
  for ( var i = 0; i < nodes.length; i++ ) {
    node = document.importNode( nodes[i], false );
    test.appendChild( node );
  }

  return test.childNodes;
});

HTML via adoptNode

This moves the nodes from the XML document, using adoptNode, and places them into the HTML document (should be case sensitive).

runTest("HTML via adoptNode", function(){
  var test = document.getElementById("test");
  while ( test.firstChild ) {
    test.removeChild( test.firstChild );
  }

  var nodes = xml.documentElement.childNodes, node;
  while ( nodes.length ) {
    node = document.adoptNode( nodes[0] );
    test.appendChild( node );
  }

  return test.childNodes;
});

The Results

I ran the following tests in IE 6, IE 7, IE 8, Firefox 3.5, Safari 4.0.3, Chrome 3.0.195, and Opera 10.10. Additionally I tested against .tagName in addition to .nodeName and found no discernible difference (you can run your own .tagName tests by appending a ?tagName to any test URL like so.)

HTML 5 Document

Note: The HTML 5, XHTML (served as HTML), and no-doctype pages all behaved identically to each other in every browser - thus I'm just going to not display the XHTML (as HTML) and no-doctype results as there wouldn't be anything interesting to show.

Firefox, Safari, and Chrome all yielded the same results here: Bringing in elements from an external document maintains the case sensitive nature of the .nodeName property - which is unexpected.

<div> <DIV> <section> <SECTION>
HTML DIV DIV SECTION SECTION
HTML createElement DIV DIV SECTION SECTION
innerHTML DIV DIV SECTION SECTION
XML div DIV section SECTION
XML createElement div DIV section SECTION
HTML via importNode div DIV section SECTION
HTML via adoptNode div DIV section SECTION

Internet Explorer fails in a different manner. To start, Internet Explorer doesn't support importNode or adoptNode so those particular tests simply don't run. However we can confirm that the case sensitivity of the unknown HTML 5 element is maintained in HTML, even though it shouldn't be.

<div><DIV><section><SECTION>
HTMLDIVDIVsectionSECTION
HTML createElementDIVDIVsectionSECTION
innerHTMLDIVDIVsectionSECTION
XMLdivDIVsectionSECTION
XML createElementdivDIVsectionSECTION
HTML via importNodeError: Object doesn't support this property or method
HTML via adoptNodeError: Object doesn't support this property or method

Opera ups the ante one further: Since it attempts to simultaneous follow web standards, and implement Internet Explorer's weird quirks, it both fails the importNode/adoptNode and the HTML 5 unknown element cases.

<div><DIV><section><SECTION>
HTMLDIVDIVsectionSECTION
HTML createElementDIVDIVsectionSECTION
innerHTMLDIVDIVsectionSECTION
XMLdivDIVsectionSECTION
XML createElementdivDIVsectionSECTION
HTML via importNodedivDIVsectionSECTION
HTML via adoptNodedivDIVsectionSECTION

XHTML (served with correct mimetype)

Nearly every browser that supported showing this page (Firefox, Safari, Opera, Chrome) displayed the same, expected, results:

<div> <DIV> <section> <SECTION>
HTML div DIV section SECTION
HTML createElement div DIV section SECTION
innerHTML div DIV section SECTION
XML div DIV section SECTION
XML createElement div DIV section SECTION
HTML via importNode div DIV section SECTION
HTML via adoptNode div DIV section SECTION

An XHTML page served properly is just an XML document - thus the case of elements is sensitive (as to be expected).

... except in Opera. Opera apparently will treat div elements case insensitively, when injected using .innerHTML, even if it's being served within an XHTML document.

<div> <DIV> <section> <SECTION>
HTML div DIV section SECTION
HTML createElement div DIV section SECTION
innerHTML DIV DIV section SECTION
XML div DIV section SECTION
XML createElement div DIV section SECTION
HTML via importNode div DIV section SECTION
HTML via adoptNode div DIV section SECTION

Update: XHTML as XML Tests

Based upon some suggestions in the comments I've run some additional tests. Namely I tested the loading of an XML document that has the correct XHTML namespace attached to it (specifically I used the same XHTML test page that I used for the other tests, just appending a .xml extension instead of .xhtml). The results are rather interesting - and promising, at least. (Note: Internet Explorer continues to fail as it doesn't have an adoptNode/importNode method.)

Firefox continues to fail the importing of XML nodes, even when they're coming from an XML document:

<div> <DIV> <section> <SECTION>
HTML DIV DIV SECTION SECTION
HTML createElement DIV DIV SECTION SECTION
innerHTML DIV DIV SECTION SECTION
XML div DIV section SECTION
XML createElement div DIV section SECTION
HTML via importNode div DIV section SECTION
HTML via adoptNode div DIV section SECTION
XML (XHTML) div DIV section SECTION
XHTML via importNode div DIV section SECTION

As does Opera:

<div><DIV><section><SECTION>
HTMLDIVDIVsectionSECTION
HTML createElementDIVDIVsectionSECTION
innerHTMLDIVDIVsectionSECTION
XMLdivDIVsectionSECTION
XML createElementdivDIVsectionSECTION
HTML via importNodedivDIVsectionSECTION
HTML via adoptNodedivDIVsectionSECTION
XML (XHTML)divDIVsectionSECTION
XHTML via importNodedivDIVsectionSECTION

BUT both Safari and Chrome PASS on the importing of XHTML nodes, coming from an XML document:

<div> <DIV> <section> <SECTION>
HTML DIV DIV SECTION SECTION
HTML createElement DIV DIV SECTION SECTION
innerHTML DIV DIV SECTION SECTION
XML div DIV section SECTION
XML createElement div DIV section SECTION
HTML via importNode div DIV section SECTION
HTML via adoptNode div DIV section SECTION
XML (XHTML) div DIV section SECTION
XHTML via importNode DIV DIV SECTION SECTION

This, in particular, is great news. It means that, at least, one browser understands the concept of loading in external (X)HTML into an HTML document and having it continue to work. It's unfortunate that it doesn't work in all browsers, though.

Conclusion

What can we learn from all of this? Unfortunately it appears as if we can't really trust our "trusted" rules about .nodeName case sensitivity for HTML documents. XML documents are completely safe and work as expected. XHTML (served with the correct mimetype) documents are nearly safe, save for the one bizarre Opera bug.

How will this change the code that we write? In short we can no longer trust the case insensitive nature of HTML documents - we need to assume that BOTH HTML and XML documents will be serving their content in a case sensitive nature - especially as more people start to adopt HTML 5 elements in their pages and expect some level of support in older browsers. This means that a number of selectors and DOM methods will take a performance hit as we can no longer take a case insensitive shortcut in our codebases.

There are a few outstanding jQuery tickets that are the result of these issues cropping up and now that I know the reasoning behind why they're happening I can now strip out all the case-insensitive performance improvements from the codebase - which is really quite unfortunate but at least it'll behave more consistently. I continue to stand by thesis from my earlier talk about the DOM: The DOM is a mess and every DOM method and property is broken in some way, in some browser.

Tags: dom, javascript, browsers

How do Mobile Browsers Behave?

One of my favorite sources of active mining is that of Peter-Paul Koch digging in to mobile browsers and how they behave. Sponsored by Vodaphone to do a study of various mobile devices and their respective browsers, PPK has been doing some serious analysis of what the landscape looks like.

Armed with a battery of tests he analyzes the various browsers manually (a painstaking task) but it yields some sweet fruit:

He breaks down the results into three areas (note: The results are very much still a work in progress, as noted by PPK, so please be aware of that before making any specific decisions on implementation or usability):

  • Mobile JavaScript/CSS Tests - These are the most comprehensive tests, analyzing a number of common DOM methods, Ajax, touch events, keyboard events, font styling, and custom events. Scanning the tables of results should be able to give you a pretty good picture of what mobile browsers are like.
  • Mobile CSS - Covering everything from CSS selectors (new and old) along with basic and advanced styling techniques.
  • Mobile W3C CSSOM - Covers aspects like screen size, mouse position, document size, and scroll offset.

In general, the results are terribly painful. The majority of the devices and browsers available today are a complete mess. Thankfully smart devices are making solid inroads bringing along good-quality browsers (WebKit-based browsers and Opera Mobile) but it's going to be a while before those devices are widely available.

Peter-Paul gave two follow-up talks on mobile browser testing, at Google and at Yahoo, that are worth watching, for additional details.

Google Talk on Mobile Browsers


Yahoo Talk (also discusses mobile browsers)


PPK is also organizing the Fronteers conference, taking place this fall in Amsterdam. I plan on attending and speaking, as well.

Tags: browsers, css, javascript, mobile

Determining Browser Market Share

A common question that I hear from developers is "What is the market share of Firefox?" (or, more recently, "What is the market share of Firefox 2?"). There are a couple answers but generally you shouldn't care about the results.

How do you determine the global market share of a browser?

It's hard to give a proper answer to this - there are so many factors that go in to determining an exact global market share. How do you track users? If you're tracking web sites, which web sites do you track? No matter how you look at it, it's hard to pin these down.

The Mozilla Metrics team has been blogging recently, discussing different ways in which market share can be evaluated. In a recent post breaking down the various metric services they looked at some of the most popular ones, analyzing their quality of data.

One of the services that we make a lot of use of to get a decent global view of browser market share is Net Applications. Their data comes from a wide selection of sources and tracks over 160 million users.


Data for Feb. 2009.

When someone is wondering what the global market share numbers look like, I generally point them at the Net Applications data.

How do you determine the market share of a specific browser version?

Again, Net Applications comes through well. They provide a data breakdown of browsers by version that works well.

This helps to answer the question that I've seen recently: "What is the market share of Firefox 2?"


Data for Feb. 2009.

Thankfully the Mozilla Metrics team released a country-by-country breakdown looking at some of the locations where users are slowest to upgrade.

Why you shouldn't care about these numbers.

The above global market share numbers should only matter to two groups of people: Browser vendors (it's nice for them to be able to know how many people are using their applications) and generic tool developers (creators of libraries that could be used by any number of people in any locale). The problem is that most people don't fall into either of these categories.

Instead, most developers should be monitoring one statistic: What is the browser share for my web site.

In order to make smart decisions about what browsers to support for your web applications you need to, first, get an accurate picture of what your users are using - any other determination will simply be incorrect and will give you a skewed sense of importance.

I talked about this balancing process last year, asking developers to balance the amount of work required to support a browser with the raw number of users that the browser provides.

I also talked about this balancing process in my recent talk at Yahoo!: The DOM is a Mess.

In fact, when determining what browsers to support for the jQuery library we use a mix of market share and intuition. We immediately support the major browsers - their current version, their previous version, and their next version. Additionally we extend support to older browser versions that still have significant market share (such as IE 6).

It's important to note that the above data has absolutely no relation to the actual visitors to the jQuery.com web site. Developers tend to present a very skewed view of which browsers are the most popular:

jQuery.com Browser Market Share
(Visitor breakdown to jQuery.com, by browser, taken on March 29th 2009.)

Tags: browsers

The Browser Operating System

A very interesting paper was just published by Microsoft Research that details a browser construction that acts more like an operating system, partitioning off resources only to those who need it.

Although our architecture may seem to be a straightforward application of multi-principal OS construction to the browser setting, it exposes intricate problems that didn’t surface in previous work, including dealing with legacy protection for cross-origin script source, display protection, and resource allocations in the face of cross-principal web service composition common on today’s web. We detail our solutions to the first two problems and leave resource allocation as future work.

In our browser design, we take the general stance that security (maintaining the multi-principal OS principles by having Browser Kernel exclusively manage the resource protection and sharing) comes before backward compatibility. We will not trade significant security risks for compatibility. Nevertheless, we will also not settle on a design that breaks many parts of the web to secure just a few sites. We present design rationales for such decisions throughout our design.

We have built an IE-based prototype that realizes Gazelle’s multi-principal OS architecture and at the same time utilizes all the backward-compatible parsing, DOM management, and JavaScript interpretation that already exist in IE. Our prototype experience indicates that it is feasible to turn an existing browser into a multi-principal OS while leveraging its existing capabilities.

With our prototype, we successfully browsed 19 out of the 20 Alexa-reported, most popular sites that we tested. The performance of the prototype is acceptable, and a significant portion of the overhead comes from IE instrumentation, which can be eliminated in a production implementation.

I wouldn't get too excited about being able to see an implementation soon - this was done by Microsoft Research (it doesn't appear as if anyone from the IE team was directly involved - this was mostly an academic pursuit). Regardless, it makes for a very-interesting read with regards to much of the technology that inhabits a web browser (DOM, CSS, etc.) and the security concerns that surround them.

Tags: microsoft, ie, browsers

Talk: Performance Improvements in Browsers

I gave a talk last week at Google (at the request of the excellent Steve Souders) all about the performance improvements, and new APIs, that are coming in browsers. I cover the new browsers, their JavaScript engines, their JavaScript performance, and then do a whirlwind tour of their new DOM methods and some of their new CSS APIs.

John Resig: Performance Improvements in Browsers @ Google
John Resig: Performance Improvements in Browsers @ Google


Presentation: Performance Improvements in Browsers

Tags: javascript, dom, browsers, html5

Talk: The DOM is a Mess

I gave a talk last week at Yahoo (at the request of the YUI team) all about the DOM. I outlined some of the reasons why the current situation is such a mess, outline some strategies for working around it, and then give some examples of real world code that's being implemented in libraries today.

John Resig: "The DOM Is a  Mess" @ Yahoo! Video
Video: John Resig: "The DOM Is a Mess"

Tags: dom, browsers, javascript

A Web Developer's Responsibility

It's safe to say that the biggest tax on a web developer is spending so much time dealing with browser bugs and incompatibilities. Thus it has become the favorite past-time of all web developers to complain about having to deal with them. Browser bugs are annoying, frustrating, and make your job incredibly difficult.

Because browser bugs are so frustrating and such a burden on top of normal development it should be the responsibility of every web developer to make sure that the browsers they develop for are able to find and fix their bugs. By taking responsibility for the bugs that you find - and to not assume that "someone else will find it" - will accelerate the rate at which browsers can improve.

The solution to helping browsers is two-fold: 1) Every time you find a browser bug, file a bug report to the respective browser. 2) Actively test your sites in the latest builds of the major browsers.

The vast majority of web developers have never filed a bug report with a browser vendor - or even used a nightly version of a browser - which is a shame. If you think about it there are few who are more qualified to assess what is going wrong in a browser than those who spend every day developing for them.

I'm especially surprised when I see professional developers not filing bugs with browsers, or testing on nightlies. Since one of the primary tasks of most developers is to paper over cross-browser issues it becomes in their best interest to see the number of bugs reduced (and making their job dramatically simpler).

I've personally filed bug reports with every major browser vendor and I've noted a couple characteristics that make for a good report.

How to File a Good Bug Report

The three points that make for a good bug report are: categorization, test case, and reduction. Any bug that is categorized correctly and provides a reduced test case is guaranteed to be reviewed by a browser developer.

Let's start with where to file the bug itself.

Filing a Bug Report

Frequently when you go to file a bug report you have to wade a couple layers deep before you can get to the actual submission form. I've provided direct URLs to the best forms to use, below.

When you're filing your bug be sure to also test it in the latest nightly of the browser you're filing against (which I'll describe later on). This will be one of the first things that a browser developer asks. If you can show that the bug still exists in the current development version of the browser, that it has not been fixed, then it'll be that much easier for them to get started.

Note: Many of the bug report pages require that you create an account before submitting a report. This is an annoying one-time cost.

Categorizing the Bug

Categorizing a bug properly is an important first step. Frequently the owners of particular modules (such as layout or DOM) watch all new submissions that come in. Assigning a bug to the correct category will instantly put it before the eyes of the very person most capable of fixing the bug.

The categorization of a bug depends on the browser (some browsers, like Opera and Internet Explorer), provide simplified categories for filing a bug while others (WebKit/Safari and Mozilla/Firefox) use complex categories to denote the specific module where a piece of functionality might exist.

Mozilla/Firefox: Choose a Component. Some of the most common ones: DOM, Layout, JavaScript Engine.

WebKit/Safari: Choose a Component. Some of the most common ones: HTML DOM, Layout and Rendering, JavaScriptCore.

Google Chrome: Figuring out if you should file for Chrome is tricky. First test your bug in both the latest release of Safari and in the latest WebKit nightly. If the bug exists only in Chrome then file there, otherwise file the bug with WebKit/Safari. One problem, though: Bugs that only exist with the Chrome JavaScript engine (V8) should be filed in the V8 bug tracker instead of the Chrome one (lest it get lost in the shuffle).

Also, Chrome does not provide an explicit means of categorization. All bugs are reviewed by a developer and then categorized (you have no control over this process).

You should also take the quick step of testing on more than one platform (OS X and Windows, Windows and Linux, etc.). Simply determining that the bug exists on more than one platform can dramatically help to reduce the time needed to locate the cause of the bug by the browser developer.

Providing a Test Case

Any type of reproducible test case is better than nothing. A web page that's able to encapsulate the problem is generally a good start. If the web page is able to be attached to the bug report directly, that's even better (it may take a while for the browser developer to get around to your ticket and if your test case no longer exists at the URL that you specified then they'll likely just close your ticket and move on).

That being said there is such a thing as a bad test case. The worst kind is something like: "I have a web site at http://example.com and it doesn't work in browser X, please fix." That will take someone a considerable amount of time to locate the exact reasons for failure and will likely push your bug much farther back on the queue.

The best kind of test case is one that provides a reduction.

Providing a Reduction

Providing a simple test case is absolutely the hardest and most frustrating part of creating a bug report - but it's also the point that'll make your report most likely to be noticed and fixed. Even for the most qualified developers it should take no more than 30 minutes to create a good-enough test case.

The process for creating one is simple: Take a page that has the bug in it and rip out anything that doesn't affect the reproduction of the bug. This includes stylesheets, images, JavaScript files, JavaScript libraries, and HTML.

For example, a while back when I was running the Dromaeo test suite and I noticed that WebKit kept crashing when it hit a certain point. I began by ripping out tests, unnecessary HTML, CSS, and images. I eventually worked my way down to a single test: Splitting a string. I then worked to strip out as much of the test suite as possible so that there were no external dependencies required.

Note the final result:

var str = "", ret, fn = [];

for ( var i = 0; i < 16384; i++ )
  str += "a";

for ( var i = 16384; i <= 131072; i *= 2 ) (function(i){
  fn.push(function(){
    ret = str.split("");
  });

  str += str;
})();

window.onload = function(){
  setInterval(function(){
    if ( fn.length )
      fn.shift()();
  }, 13);
};

What was left was about as simple as you could get while still having the crash occur. This is a good reduction. Based upon this reduction the reason for the problem was quickly identified and resolved just a couple weeks later.

Is my bug being worked on?

This is a challenging point - but one that's easier to determine with Mozilla/Firefox, WebKit/Safari, and Chrome (since they're all relatively open projects). Here's the best way to determine the status of the bug with each of those browsers.

Mozilla/Firefox: Your bug will start out being assigned to the default contact for the component category that you originally selected. This doesn't mean anything, yet, it's simply attracting people in to look at your ticket. People will start to CC themselves in to the ticket (which means that they're interested in its progress). The defining moment, though, is when someone assigns the bug to themselves, effectively stating that they are taking responsibility for the status of this bug. Most contributors have it set up so that any future comments on the bug are automatically emailed to them so if you have any questions about the status of the bug you can feel free to post a comment - but please do so at a reasonable pace (asking for daily, or even weekly, updates would be frustrating).

WebKit/Safari: WebKit uses a very similar setup to Mozilla/Firefox - just look for someone to take control of the bug and drive it to completion. The golden ticket, though, is when your bug "goes to rdar". Radar is Apple's internal (private) bug tracker. Having your bug move to there means that your bug is, effectively, guaranteed to be completed at some point (if not by the person who 'owns' it then by another Apple employee). Since Apple is still the major driving force behind WebKit updates having your bug move to rdar is what you should be hoping for. That being said, since rdar is private to Apple employees you no longer get the benefit of knowing if or when your bug will be completely fixed - it's just a waiting game.

Chrome: Chrome uses a system very similar to Mozilla/Firefox. Be sure to keep up communication with whom the bug is currently assigned to and to answer any questions that they might have.

If your bug eventually gets resolved then congratulations! You just helped to make the web a better place for everyone.

But that's not always the case.

What happens if my bug gets rejected?

Rejected bugs fall into two categories:

  1. It was rejected because it was not a bug.
  2. It was rejected because they browser vendor does not feel like working on it.

The first point is broken down into two further sub-categories:

It's legitimately not a bug. Congratulations! You've learned something about a standard, or some other browser obscurity, that you weren't familiar with: you're now a better web developer! You should fire up your blog and write about the obscure new bug or API that you discovered and explain it to the world.

Or it is a bug and the owner unnecessarily closed it. At this point you need to argue your case to re-open it.

The second point (the vendor does not feel like working on it) is also handled in two ways:

First, argue your case for the bug. This should help inform the browser developer that they should dedicate valuable resources to fixing this issue.

Or second, if they are truly unwilling to fix the bug: Raise holy hell on your blog, Twitter account, and any other place where other web developers will listen to you. If you can't find anyone else who agrees with your plight then you are probably crazy - but if it's a legitimate problem that the browser vendor is refusing to fix then you should easily find others with whom you can band together and complain openly to. But that's OK: You've earned this right. By doing all the due diligence necessary to bring this bug to light you earn full privileges of bitching about it in every other sentence.

Arguing a Bug

So you're at the point where your bug has been closed and you need to convince the closer that this was a mistake - that what you have is an actual, legitimate, bug.

Here are some of the best arguments that you can use, in order of which they should be used:

  1. Show that the bug was a regression. Prove that it was something that worked in a previous release that stopped working because of a change. Works great in conjunction with #2.
  2. Show real-world web sites breaking. If you can show that actual users are going to no longer visit X bank in Poland or Y shopping site in Canada then browsers will easily bend over backwards to fix the issue (unless it's Opera, in which case they may use their browser.js to force a site to work - but that's another story).
  3. Show a web standard that is being violated by not fixing this bug. If you're able to show that the W3C DOM specification is not implemented correctly because a certain bug is not fixed then a browser vendor should feel compelled to fix it. If not, this will make for great blog fodder.
  4. Show that not fixing the bug makes a browser incompatible with other browsers. If IE, Safari, and Opera all implement a specific feature or fix a specific bug then Firefox should be compelled to comply with the other implementations (as long as its not in contradiction with a specification). This is the hardest one to argue - but it becomes easier the more browsers that are on board.

If you can't prove any of those steps then you're probably just scratching your own itch anyway - and should lay off.

Examples

I want to show some representative examples of bugs that I've filed with different browser vendors.

WebKit/Safari

Canvas arc() with radius of 0 throws exception: Calling the Canvas arc() method was throwing an exception. I provided a super-simple test case and pointed to the specification that they were in disagreement with. Was resolved the same day it was posted.

Out of Memory Error with .split("") due to large number of live objects: Provided a simple reduction with hosted web page, was fixed about 3 weeks later.

Mozilla/Firefox

Huge Speed Drop in Array.prototype.sort: Showed a regression with a simple reduction. Included a bunch of very-useful Shark profiling data to help pinpoint the exact issue. Was fixed within a month.

Implement .children: Argued that Mozilla needed to implement the .children method (which was in every other major browser). A good debate led to its eventual inclusion (about 6 months later).

Internet Explorer 8

querySelectorAll NodeList Exception: Provided simple test case, was rejected due to 'lack of time':

At this time we do not plan on fixing this issue. We appreciate the report, but unfortunately we are at a stage where need to choose what we work on to maximize the value for customers and web developers.

So while they agree that it is an issue - they do not plan on fixing it. That means that I now get to complain about it!

HTMLElement.prototype.querySelectorAll doesn't exist: As it turns out this was because querySelectorAll only exists in standards-compliant pages (not in quirksmode). This is positively bizarre but I think I understand their rationale behind the decision. I suspect that this will bite a lot of people once IE 8 goes live, we'll see. I now know more about IE8 and am a better developer for it.

Google Chrome

for in loops don't occur in defined order: This was a compatibility issue (all other browsers behaved in a particular manner). I provided a simple test case. It was accidentally closed as WontFix (which caused confusion) but was actually fixed. I made a mistake here and actually filed this bug against Chrome when it should've been against V8 - here's the bug that dealt with the issue over there.

setTimeout(..., 0) fires too quickly: This was actually due to a structural change made by the Chrome team. Mike Belshe (the author of the change) personally emailed me to explain what happened. I became much more informed as a result and blogged about it.

Opera

(Opera does not provide an official public view for bug reports, that I know of.)

How to Test a Nightly Build

Before we get into the details of testing a nightly build of a browser I should probably answer the most common question: Why should I care about testing a nightly build of a browser? There are a couple reasons.

First, when filing a bug report you're going to need to determine if the bug you're submitting has already been fixed, or not. If it's already been fixed in a nightly then you don't need to worry about submitting it - the bug will be fixed in the next release. If the bug has not been resolved, though, then you should be sure to continue filing the bug.

Second, you should periodically test your site or library in the latest browser nightlies - to make sure that your code isn't going to break when the browser is released. How often you test is up to you - but the more frequently you test the more likely it is that your site or library won't hit a massive regression at some point. I think it's pretty safe to say that no developer likes finding out that there's a new browser version on the market that breaks their site.

Filing a bug for a nightly is just like filing any other bug. Provide a reduced test case and be sure to emphasize that a regression occurred. If you're testing frequently enough this should be sure to get the developers hopping into action.

Getting the Latest Nightly

Browser vendors provide a variety of techniques for getting the latest version of a browser. Some browsers release more frequently than others, as well (for example, Chrome updates multiple times a day, Firefox once a day, and Opera every couple weeks or so).

Mozilla/Firefox

Mozilla provides nightly releases of Firefox. It can be installed, and used, alongside your existing copy of Firefox using Firefox Profiles. Once you download one nightly release it should update itself, automatically, every day.

Download: Firefox Nightly Release

WebKit/Safari

WebKit nightlies are easy to install on OS X - they can live completely side-by-side with no profile details. However they do not update automatically. I use NightShift to make sure that my WebKit nightly is kept up to date (on OS X).

Doing a nightly install on Windows is much more cumbersome (it involves running some scripts and copying files around) but it works.

Download: WebKit/Safari Nightly Release

Internet Explorer

Internet Explorer installs are "a big deal" - they completely blow away any previously-installed copy of the browser. For this reason you should be sure to use some tricks to keep multiple copies of Internet Explorer installed on your system. There's one installer that can handle IE 6 (and older) and another one for taking care of IE 7. Once you have all those standalone versions installed you can feel safe downloading and installing IE 8.

IE 8 automatically updated from each beta release but it doesn't appear to do it any more. If you sign up for the Microsoft IE Beta Connect program you can get more recent builds to test against. Again, all of these builds will overwrite older versions of the current browser.

Download: IE 8 Betas, IE 8 Weekly Builds

Google Chrome

Google provides multiple builds per day (one for each revision). These builds can live side-by-side with one another but they do not update automatically. One user built an automatic update application that you can use to make that happen.

Download: Google Chrome Nightly Builds

Opera

Multiple versions of Opera can be installed side-by-side and they update automatically. They don't provide nightly builds (they come out every couple weeks, or so) but they should serve as a relatively-current example of the browser.

Download: Opera Desktop Team Blog (builds are posted here)


The importance of taking an active role in the future of web development cannot be overstated. Shifting from a passive position of hoping that other developers will be proactive about filing bugs or hoping that browser vendors will notice every possible regression to one of active diligence gives you an incredible amount of power. The minimal amount of work that you do to improve communication between the web community and browsers does volumes for helping to improve the quality of the entire web.

You should wear every bug that has gotten fixed, because of you, as a badge of honor: You've done your part to make the web a better place.

Tags: browsers, bugs, testing

The March of Access Control

The web is changing. Historically it's been painfully easy to request resources from remote locations (such as stylesheets, scripts, images, and loading pages in iframes) - but this has brought along a whole world of security issues that browsers are continuing to try and resolve.

This openness has come to define what web development is all about: Dead simple sharing of resources and ability to get started. It's very likely that the lack of restrictions placed on these historical page elements will continue to plague browser developers for many years to come.

That doesn't mean that browsers have to make the same "mistakes" going forward.

This is where the new W3C Access Control specification comes into play.

All the new cross-domain-capable technologies that are coming to browsers will be requiring the use of Access Controls from the get-go, including:

The Access Control specification has been one of the most-rapidly-changing specifications that I've seen. I wrote a demo early this year and have had to update it at least twice since then in order to match the updated APIs - and it appears as if they may have even changed again.

Right now the specification requires that any resource that you wish to make accessible in a cross-domain manner must include an extra header specifying which site(s) are allowed to access it.

If you wish to allow any site to access your resource you would use:

Access-Control-Allow-Origin: *

and if you only wanted one other domain to access it you would use:

Access-Control-Allow-Origin: http://ejohn.org

This is important: It now means that site owners must make a conscious decision to enable cross-domain access of their resources (in contrast to images, stylesheets, and scripts which are always made available cross-domain with no way to disable it).

(There are a number of other headers specified by the Access Control specification, for more fine-grained access.)

There is going to be a lot of confusion and anger regarding this large, fundamental, change to the style of these upcoming APIs: They aren't like the web that we know and love!

The best write-up that I've seen, to date, was by Jonas Sicking of Mozilla on the Ogg Mailing lists. This one section, in particular, is particularly poignant:

Why not use the same policy as for <img>?

Yes, we could definitely do the same for <video> as we have for <img>. But it will come with the same downsides. It will mean that we will have to be much more cautious with how we develop the API for

There are already discussions about API features that we could not allow if we allowed cross-site video without Access-Control (or similar) protection. We would not be allowed to have callbacks for captions where the captions are handed to page javascript to be displayed in the page. This would allow an internet site to get captions from board presentation videos hosted on intranet sites, something that is obviously not acceptable.

We could say that the captions callback would work, but only if the video was loaded from same site, or if had the Access-Control-Allow-Origin:* header set. However this will likely result in random bugs like captions sometimes failing since the developer had perfect hearing and so didn't do a lot of testing with captions. In general accessibility is hard enough to get people to do correct that I'm reluctant to add features that work great as long as you don't take accessibility into account, but where you have to take extra steps to get accessibility to work.

Similar arguments goes for accessing the size of the video file (for example through progress events). We can not allow that to work for cross-site loads unless the site has opted in. This is because we likely won't know that it's actually a video that is being downloaded until after the first progress events have been fired. This means that you could use <video> to measure file sizes for arbitrary files that are otherwise protected by firewalls and/or logins.

If we always restrict usage of <video> to the cases when we know that the video is private data we will be much more free to develop APIs and functionality since we won't have to worry about protecting the data inside it, or deal with error conditions when someone tries to use sensitive APIs from a cross-site loaded video that didn't have Access-Control-Allow-Origin:*.

I recommend that you take the time to read his whole piece as it's worth it to gain a full understanding of the problems at play here (especially related to the <video/> tag).

One thing is clear: Security is being addressed center-stage in the new web APIs. This is going to be good as it'll prevent horrible security bugs going forward while, at the same time, change the landscape of web development in a very fundamental way. The web had its fun but now reality is starting to set in - it's time to get to work.



I'm reminded of the recent release of a crazy hack: transmitting data via URL encoded strings in stylesheets, named CSSHttpRequest. It's an insane technique (in the best possible sense of the word) and well outside the realm of most users. Even though the syntax and technique is different, the security/information-leak implications of this are every-bit as real as those presented by JSONP.

Tags: browsers, w3c

· « Previous entries

JavaScript Books

Secrets of the JavaScript Ninja

JavaScript Secrets

Secret techniques of top JavaScript programmers.

Pro JavaScript Techniques

Pro JavaScript

The best techniques for professional JavaScript. Published by Apress.

Micro Updates

John Resig Twitter Updates

@jeresig

Infrequent, short, updates and links.

JavaScript Jobs



Hosting provided by: Ruby Hosting by Engine Yard