Firefox’s proposed Resource Packages spec blows!

6 min read Original article ↗

It’s no secret that one of the keys to a decent user experience is a responsive (fast) user interface. And the most critical component to a fast web app is minimizing the HTTP requests. So it’s no mystery that people are working on ways to solve this problem.

Firefox, of course, is one of the leaders in this area. Their proposal started out fine. Here’s a sample of what it would look like (if you didn’t read the article):

<link rel="resource-package" 
      type="application/zip" 
      href="/static/site-resources.zip"
      content="javascript/jquery.js;
               css/reset.css;
               css/grid.css;
               css/main.css;
               images/save.png;
               images/info.png" />

<link rel="resource-package" type="application/zip" href="/static/site-resources.zip" content="javascript/jquery.js; css/reset.css; css/grid.css; css/main.css; images/save.png; images/info.png" />

You specify that the file, /static/site-resources.zip”, contains the listed resources. JavaScript, CSS, and images. (I imagine SWFs and videos would also be options.)

But for some reason, this concise, nearly-valid implementation was trashed in favor of a terse, unfamiliar, and nearly unusable alternative. Here’s what the spec looks like now:

<html packages='[pkg1.zip img1.png script.js styles/style.css]
                [static/pkg2.zip]'>
</html>

<html packages='[pkg1.zip img1.png script.js styles/style.css] [static/pkg2.zip]'> </html>

In this bastardization, the packages are specified as an attribute on the document element (<html> tag). Package definitions in the packages attribute are delimited by square brackets, and the resources within them are delimited by spaces.

WTF? Not only is this ugly and non-standard, but it also has some serious drawbacks. Here’s why:

Reason #1: Any non-trivial web app has hundreds of resources

The spec says that naming the resources is optional. However, not naming them causes the browser to block all further downloads until the resource package is downloaded and unzipped. So to gain optimal performance (not block), we’ve got to list every single resource? Not only is that going to be one hell of a large <html> tag, but it also needs to be maintained every time a new resource is added to the app.

The real answer, of course, is to use a dependency-management and packaging solution. dojo.require and dojo.build do all of the work of resource packaging already. (require.js is a good start at dependency-management for the non-dojo world, but still lacks packaging. Admittedly, dojo only packages javascript and html templates, not css or images. cujo.js will also package css, but still not images.)

With dependency-management, resources are named where they are used, not in some other file. Having to remember to go back and list every resource in every HTML file is nothing short of a maintenance hassle.

But hmmmm. Maybe this is a not as big an issue as I originally thought. If I am using a dependency-management solution, my resource packages would consist of one javascript file, one css file, and a few dozen images. That’s still a lot of image files to maintain, but not as bad as maintaining hundreds of javascript and css files.

One of the reasons the browser needs to block is to allow for overrides. If package, pkg2.zip, has a resource with the same path/name as a resource in a package declared earlier, pkg1.zip, then the resource in pkg2.zip will override (replace) the resource in pkg1.zip. Therefore, the resources in the packages must be delineated — either explicitly or by inspecting the package — before the packages can be used.

Again, this override functionality is just duplicating dependency-management solutions. Object-oriented javascript allows us to override methods and properties. OOCSS allows us to override CSS rules in a different, but equally effective, manner. (cujo.js also allows for HTML template inheritance / overrides (OOHTML?).)

When done properly (small, concise OOJS, OOHTML, and OOCSS files) and combined with dependency-management, the download size should be exactly the same and amount of extra RAM needed by the browser should be small compared to the resource packaging solution.

dojo.require('pkg1'); // base functionality (large)
dojo.require('pkg2'); // overrides (small)

dojo.require('pkg1'); // base functionality (large) dojo.require('pkg2'); // overrides (small)

Reason #2: There’s no way to specify a resource package dynamically

This is a non-starter for me. I write client-heavy web apps. These apps are typically long-running and are served to the browser as a single page. In other words, the browser doesn’t load a new page every time the user takes an action. Google Docs and Google Mail are good examples.

Using dojo.require and dojo.build, I can package my apps into as many packages as I like. (They’re called “layers” in dojo.) This is especially important in a few scenarios:

  1. the app will load a package only when needed
  2. the user can switch “theme” packages dynamically
  3. the user can switch language packages dynamically

If Firefox goes forward with the packages attribute on the <html> tag, then I don’t see how this is possible. For example:

var html = document.documentElement,
    myNewPackage = 'pkg3.zip js/myModule.js images/myPng.png';
html.setAttribute('packages', html.getAttribute('packages') + ' [' + myNewPackage + ']');

var html = document.documentElement, myNewPackage = 'pkg3.zip js/myModule.js images/myPng.png'; html.setAttribute('packages', html.getAttribute('packages') + ' [' + myNewPackage + ']');

Even if that did work (which I doubt it would), it would undoubtably be unbearably inefficient. Re-parsing, re-scanning, and re-analyzing every package.

The original proposal did not have this problem. It’s a lot simpler to just add a new <link> tag:

var head = document.documentElement.firstChild, // assumes no comments before head!
    link = document.createElement('link');
link.setAttribute('rel', 'resource-package');
link.setAttribute('type', 'application/zip');
link.setAttribute('href', 'pkg3.zip');
link.setAttribute('contents', 'js/myModule.js; images/myPng.png');
head.appendChild(link);
 
// in dojo:
var head = dojo.query('head')[0];
dojo.create('link', {rel: 'resource-package', type: 'application/zip', href: 'pkg3.zip', contents: 'js/myModule.js; images/myPng.png', head, 'last');

var head = document.documentElement.firstChild, // assumes no comments before head! link = document.createElement('link'); link.setAttribute('rel', 'resource-package'); link.setAttribute('type', 'application/zip'); link.setAttribute('href', 'pkg3.zip'); link.setAttribute('contents', 'js/myModule.js; images/myPng.png'); head.appendChild(link); // in dojo: var head = dojo.query('head')[0]; dojo.create('link', {rel: 'resource-package', type: 'application/zip', href: 'pkg3.zip', contents: 'js/myModule.js; images/myPng.png', head, 'last');

So. Can it be fixed?

Of course. But, frankly, I don’t really care. For us dojo developers, all it’s going to help out with is images. And since we’re using less and less images and more and more CSS3, images aren’t such a big deal going forward. (border-image, border-radius, and gradient, FTW!)

But I guess, for image-heavy apps, there’s still a need. So here’s how I’d fix it:

First of all, abandon that dumb-ass packages attribute. It’s ugly, non-standard, and unworkable for the large apps that really need packaging. Go back to the <link> tag. That was a much better idea.

Or better yet, use the <script> tag. (gasp) And while you’re at it, fix the defer attribute so I can decide whether the package contains any resources that may need to invoke blocking. (It could be a package of non-essential images, for instance.)

Hmmmm…. on second thought, the <link> may be semantically and syntactically better. Therefore, maybe just add the defer attribute (and functionality) to the link tag?

Should it be fixed?

The more I think about the proposed solutions, the more it seems they’re targeted toward trivial web apps/pages. Unfortunately, it’s typically the larger, more sophisticated apps that need the performance boost.

Therefore, I say, why bother?

Instead, let’s find a way to package images and make dependency management a standard feature in javascript!

What do you say? Am I missing something? Do you have a better solution? Please let me know in the comments!