Sunday, November 14, 2010

RequireJS 0.15.0 Released

RequireJS 0.15.0 is available for download. This is a bug fix/robustness release:
  • The bundled jQuery options now use jQuery 1.4.4.
  • The jQuery sample project now includes the ability to use RequireJS plugins.
  • The jsonp! plugin has been removed, since, thanks to work by Kris Zyp, the core loader now supports loading JSONP dependencies by default. The JSONP docs have been updated accordingly.
  • The optimizer can now be run from any directory, not just the directory with the build profile.
  • r.js Node adapter is more robust, and it can handle using more Node-written modules by default now. Thanks to Francois Laberge for a great test case application that lead to improving the robustness of r.js.
  • Initial support for PS3 Netfront browser. Thanks to Chris Warren for investigating the load behavior of the browser. Not all tests pass, but the basic ones do.
  • Miscellaneous fixes, some listed in the issue tracker.
The next stage of development will likely be a refactoring of the basic code. The code has grown organically along with some changes in requirements over the course of the year, and it would be good to reorganize based on the current understanding of script loading needs. The biggest noticeable change should be a better API for writing loader plugins, but the core API for requiring and defining modules should not change.

Friday, November 12, 2010

The Open Source of Mozilla F1

I am a front end developer on Mozilla F1, a sharing extension in Firefox, and this is some technical background on it. Note that any opinions in here are from my personal perspective, you should not treat this as official Mozilla policy, etc...

F1 consists of a few components:

1) Firefox browser extension
2) HTML/JS/CSS share UI served from from the F1 web server
3) API server written in Python that lives on the F1 server

The extension (#1) is responsible for the share button in the toolbar, injecting navigator.mozilla.labs.share(), and for managing a browser instance that is inserted above the main web browser area. This browser area makes a call to the F1 server to server up the main share UI (#2).

The share UI is written in pure HTML/CSS/JS. They are static files that use RequireJS to build modular JavaScript which is then optimized via the RequireJS optimizer into a single application-level file. It does not yet use appcache, but it is something I want to add. It should be straightforward. Making sure we properly version files with long cache times will also help.

The share UI uses the API server (#3) to do the actual sharing. The API server also manages the complications of the OAuth dance.

If you are curious about the implementation, all the code is open. You can download it/fork it at the Mozilla Labs F1 GitHub repository. There is a README.md file with setup instructions.

The great thing about this? You can run your own share server that you control. If you want the F1 browser extension to use your server for the share UI and API server, set the following F1 configuration in the about:config Firefox preferences:

extensions.ffshare@mozilla.org.share_url = 'http://your.server.com/share/'

Ideally that would be an https URL, but if you are setting up your own server, hopefully you know the risks involved. Be sure to restart the browser so the extension will use this new config value.

If you think this is really cool and want to help contribute code/ideas/submit pull requests, be aware that we are still in the early stages of F1. We expect to make many changes as we refine it. Mozilla is also new to GitHub, so we appreciate your patience as we try out different work flows.

I will close out this post with a couple developer bikeshed questions and answers. Again, these are my personal perspectives, and other people on the team may have different ones.

Why is the share UI served from a web server and not from the extension?

This is an experiment. More things are moving to the cloud/server, and some of the auth APIs, like OAuth, are better suited to server use. While Mozilla already runs some non-trivial server-based services, it is good to get experience with varying types of server-based services.

I like the flexibility and ease of updates a server solution provides. Also, it is easier to debug web code that is served from content space vs. browser chrome space.

It may allow us to support mobile and even other browsers easier. I played around with doing a Google Chrome extension that served up the share UI. Unfortunately, the Google Chrome extension model is not as flexible as what we can do in Firefox, so it does not really work.

In particular, I was looking at a Chrome extension Browser Action that served up the UI in an iframe in a Popup, but I could not get the popup wide enough to show the UI, and it closes as soon as an OAuth window jump occurs. We would have to do some rethinking on UI to support that, and it is not clear how beneficial that is at this early development stage.

All that said, the UI could be burned in to the extension at some point. It is still too early to tell where this will go.

Why did you use Python for the server?

Ease of setup, combined with some knowledge of real services built with it, and the mix of developer skills of the F1 team.

Ideally I would like to see the server running JavaScript via Node, but that is because I am an irrational JavaScript zealot. However, the JS libraries for Node are still young, and at the end of the day we want to solve real user problems, not debug other people's code.

That could change in the future. But for now, it is more important to get something up quickly that works well so we can test our designer's usability theories for solving real user problems.

HTML5 and Script Execution Order

As the latest browser renderers, WebKit, IE9 and Firefox 4, implement HTML5 there is wording in the HTML5 spec that breaks ordered execution of dynamic script elements.

What does this mean in practice? The RequireJS order! plugin and a core feature of LABjs breaks in the latest browsers under development. More info:

When a dynamically created script element is created and appended to the DOM (via document.createElement('script'), the behavior in the current browsers differs: IE and WebKit will execute the script as soon as it is delivered from the network, where Opera and Firefox will download the script as fast as it can, but execute the scripts as they are ordered/appended to the DOM.

The Firefox/Opera behavior is desirable for making existing scripts on the web go fast since all the scripts can be downloaded in parallel, but still execute in the order you specify -- there are many scripts today that assume their implicit dependencies have already been executed before the script executes.

jQuery plugins that assume jQuery is already available in the page are a good example.

Kyle Simpson of the LABjs project figured out a nice hack for IE and Webkit to get them to execute scripts in order. He gets IE and WebKit to download the scripts first without executing them (via a non-script script type, like type="text/cache"), then adding the real scripts to the DOM in order normally means they execute in order.

However, this technique has edge cases where it can fail, in particular with poor cache headers, and the latest version of the HTML5 spec effectively disallows the hack. The code to get ordered execution right now also has some browser sniffing to get it to work, which is clearly not ideal.

The ideal case is to have a capability that can be sniffed in the browser, and something that would allow for ordered execution of dynamically added script elements without the need of browser hacks.

Kyle has been working with HTML5 spec folks to try to work out something along those lines.

However, the HTML5 spec participants are not sure how important of a use case this is to support to warrant a spec change. I do think it is important for existing scripts to get some performance benefit.

While RequireJS does not rely on this behavior for its core operation (the define() function wrapper ensures that it is not an issue, and assumes out of order script execution), the order! plugin in RequireJS does depend on this behavior.

So, if you are a RequireJS order! plugin user and you really depend on it, or if you are a LABjs user, please take a moment to let the HTML5 group know the sites you work on that would be affected if those tools no longer worked in the browsers being developed today.

Kyle Simpson has set up a wiki page to describe the problem. There is a Discussion section of the page where you can voice your feedback. Click the + in the tabs at the top to add a section where you can list how this issue would affect your sites. Be sure to leave your name in case the group would need to get more information from you.

Please take a moment if you do depend on this feature to let the HTML5 group know.