Wednesday, April 01, 2009

Custom local variables using the Dojo Loader

Recently, Dion Almaer was wishing for a way to avoid typing "dojo" so many times in the Bespin code. He appreciated the namespace protection that Dojo gave, but did not like the typing tax that came with it. He talked to Alex and Pete about it. Alex suggested a way, but it is very experimental.

Here is another way that leverages the strength of the Dojo Loader.

The Dojo Loader handles the work of loading your JavaScript modules. When you do a dojo.require("foo.bar"), the loader figures out that you want to load the foo/bar.js file and loads it for you. Normally the loader uses a synchronous XMLHttpRequest (XHR) call to load the file and uses eval() to bring the code into existence.

There is an xdomain loader that does not use eval, and allows your code to be loaded from any domain (with dependencies properly loaded), but it requires a build step using the Dojo build tools.

Both versions of the loader (normal and xdomain) allow you to namespace your code -- so you can map dojo to mydojo, and even your own code to some other name. This allows you to load multiple versions of dojo and/or your code in a page. Nifty.

The Loader is able to support this scope mapping by wrapping your module in a function call like so:
(function(dojo, dijit, dojox){
//Module code injected in here
})(dojo, dijit, dojox);
We can use this new scope created by this function to create local variables for your module.

Dojo's Loader can be modified to allow you to specify a set of local variable names that get injected with this function wrapping. And we can do this on a per-module prefix basis, so your code can have your own local variables, but some other module can specify a different set.

I did a prototype that works as follows: assume "coolio" is the namespace I use for my modules. I create a coolio.locals that has the following content:
dojo.provide("coolio.locals");

dojo.setLocalVars("coolio", {
trim: "dojo.hitch(dojo, 'trim')",
$: "dojo.hitch(dojo, 'query')",
id: "dojo.hitch(dojo, 'byId')"
});
This code will create local variables called trim, $ and id that map to dojo.query, dojo.byId and dojo.trim respectively, but only for coolio* modules.

Then I have another module, called coolio.actions that uses these variables:
dojo.provide("coolio.actions");

coolio.actions = {
init: function(){
$("#trimButton").onclick(coolio.actions, function(evt){
id("trimOutput").value = trim(id("trimOutput").value);
});
}
}
Notice that trim, id and $ were not declared in here. The final bit of magic is to use djConfig.require in the HTML page to auto-load the coolio.locals module before any other coolio code, so that the loader knows to create the local variables for any coolio.* module:
<script type="text/javascript" src="dojo/dojo.js" djConfig="require: ['coolio.locals']"><script>

See it in action with this sample page.

I created ticket #9032 to track the possibility of allowing this in the future. It also has the patch that can be applied to the Dojo source to get it to work. Or, if you are using Dojo 1.3.0, you can grab these built files with the changes: dojo.js or dojo.js.uncompressed.js

There are some caveats to this prototype:
1) We really need a build step that would inline the local variables for your build layers, so the code as-is will not work with custom build layers.
2) Does not work with xdomain builds yet, another tweak to the build system is needed for that.

If you think this might be useful for you, feel free to add your comments to the issue tracker entry or leave a comment.

Sunday, March 29, 2009

Job Transition

Friday was my last day at AOL, just shy of 13 years. There were good times, bad times. Some really cool things. Neat people and teachers. A great learning experience.

I am being a bit melodramatic, but Sinead O'Connor's Thank You for Hearing Me expresses the sentiment best, including the end of the song. For work and for life.

Monday I start at Mozilla Messaging. I am really excited about the work. It is still being defined, so nothing to share yet, but I look forward to helping people take charge of their messaging.

Saturday, March 28, 2009

Namespaces, Subjects and Verbs in JavaScript

It has been interesting to follow Dion Almaer's comments about using Dojo in Bespin. This post mentioning call conventions higlighted something I have wanted to talk more about.

The basic issue is namespacing and how you like to call functions. Here are some choices:
  1. subject.verb()
  2. namespace.verb(subject)
  3. namspace(subject).verb(), which can lead to namspace(subject).verb().verb().verb()
subject.verb()
#1 is the Prototype/Ruby way. You define a verb on the object's class, or in JavaScript, on its prototype.

In this model the namespace is really the subject's namespace. There is a possibility of collision with other code that wants to use the same verb. This can be mitigated by keeping your project small and focused, and managing your dependencies. This also works well when your code is a leaf node, or one step from the leaf node -- your code is not consumed by other code, except maybe a top level web application.

The benefit is a nice call structure, and I think fits better with normal English. The subject is identified and then a verb/action is performed with that subject.

Unfortunately, in JavaScript, there can be problems adding things to basic prototypes, like Array, String (and shudder, Object). In Dojo we have had to put in some protections in our code in case the page also uses code that modifies the built-in prototypes.

I believe the situation will improve in future JavaScript releases if added properties/verbs can be marked as not enumerable. That will help a bit, but namespace collision is still an issue.

Some browsers now have native String.prototype.trim implementations and Dojo now delegates to them for Dojo's trim method. Recently, there was a bug filed for Dojo where the core issue was some other code adding a String.prototype.trim() method that did not strip out beginning whitespace.

namespace.verb(subject)
#2 takes the position that verb collision is bad and can cause hard to trace bugs, so always use your own namespace. It also feels more "functional" or procedural: use small functions that do not maintain interior state but operate on their arguments.

This has the benefit of being safer, but it can be more verbose than #1, and therefore more of a constant tax on the developer.

This is mitigated somewhat in Dojo, where you can assign Dojo to another namespace. So you could map "dojo" to "$" to cut down on the typing.

namspace(subject).verb()
#3 is a nice compromise: Define a function that wraps the real subject in an object and provides verbs to act on that subject, without directly modifying the object/class structure of the subject. jQuery took this to new heights by allowing chaining of the verbs. Similarly, dojo.query() returns a dojo.NodeList object that has chainable verbs on it.

An explicit namespace is involved, but the idea is to make it as small as possible, "$". So the overhead for having the namespace is "$()". Coupled with the chainable verbs, it can lead to short code, less of a tax on the developer.

For Dojo, I think #3 is the right approach in general: it gives nice namespace protection, but still gives short call structures.

I want to explore a dojo() function that does this verb mapping in general for all of Dojo. Dijit might be able to use a dijit() to get something similar for the verbs it exposes in its namespace.

So for instance: dojo({foo: "bar"}).clone() would act the same as dojo.clone({foo: "bar"}). Scopemap dojo to $ and you get $({foo: "bar"}).clone().

The difficult part is how to deal with verbs/methods that deal with Strings. I want dojo("div") to actually call dojo.query("div"). But how to allow dojo(" some string ").trim()?

Maybe not map dojo("string") to dojo.query("string"), but instead, allow scope mapping of "d" (or even "_"?) to dojo and another mapping of "$" to dojo.query. That would probably match best with the expectations of what $ does today in other toolkits.

I will have to do more exploration. Eugene Lazutkin's work on providing adaptAs* functions for mixing in dojo methods into an object prototype as chainable methods might point the way to do this.

Thursday, October 02, 2008

Dojo 1.2 Loader and Build System

Dojo 1.2 is now available! Check out the release notes for more detailed information.

I attended the Dojo Developer Day in Boston on Sunday 9/28 and the Dojo Community Day at the Ajax Experience conference on Monday 9/29. It was really nice of the Ajax Experience folks to set up community time for the JavaScript toolkits.

During the Community Day, I gave a short presentation on some new things in Dojo 1.2 with the Dojo loader and the build system. I tried using the Google Docs presentation thing. You can see the slides here (some notes are after the slides):



customBase build option
The final example in the slides demonstrates how you can create a 5.5 KB (gzipped) dojo.js file and dynamically load it after the page loads, so if your site uses progressive enhancement, your up-front Dojo cost can be zero bytes, and just load as little as possible for the functionality that you use.

The 5.5 KB dojo.js file is possible using the "customBase" property of the dojo.js layer in a build profile. Normally we encourage you not to tamper with the contents of dojo.js (what is referred to as Dojo Base), but the customBase build option can give you more control on what parts of Dojo Base you want to load. With the customBase option, the build will go through all the JS files and automatically add dojo.require() calls for the necessary dojo._base modules.

Only use customBase if you keep all your JS code in JS modules -- good practice anyway, and keeps with progressive enhancement suggestions. Do not use customBase if you have Dojo calls inside HTML source.

Using customBase on the "dojo.js" layer with no dojo._base modules (just the loader and some bootstrap functions), the non-gzipped size of  dojo.js comes out to be 13KB (recall from above it is 5.5 KB gzipped). So it is small enough to fit under the 25KB non-gzipped size limit that the iPhone/iPod touch use to allowing caching.

You will likely need to use more of Dojo Base than what is in the 13KB non-gzipped file, but you can tune that by creating more layers and playing with the build to so that all layers are under 25KB.

djConfig.addOnLoad
Another option for use with progressively enhanced web sites, or when you want to load Dojo after page load: djConfig.addOnLoad allows you to set a function to run after Dojo loads. It is most useful when used in conjunction with djConfig.afterOnLoad (used when you manually load Dojo after page load).

Here is a complete example showing djConfig.addOnLoad, djConfig.afterOnLoad and djConfig.require.

There is a bug about using djConfig.require in conjunction with a dojo.js generated via a customBase build, so only use djConfig.require when using a complete Dojo Base file.

Thanks go to Matthew Russell for suggesting djConfig.addOnLoad for the 1.2 release.

stripConsole
There is a new build option for stripping out console method calls. stripConsole=normal strips out all console.* calls except console.error and console.warn. stripConsole=all strips all console.* calls.

Safari 3.1 sometimes has an issue with console.debug not getting defined (fixed in webkit trunk), so this build option can help avoid that issue until Safari is fixed.

Saturday, June 14, 2008

Mobile Development

I was recently asked about my experiences making TinyBuddy IM. Here are my very biased thoughts about it and mobile development in general.

My Background
I like to make web products that people use. I am a front-end developer, and I love using JavaScript, HTML and CSS. I have done some C/C++ for a cross-browser streaming audio browser plugin. I recently played with Objective C as part of an iPhone SDK experiment. I did Java on the server for a few years too.

HTML, or some sort of declarative markup, is the way to code front end UI. It clearly shows the nested relationships between components. However, I liked the Interface Builder approach in the iPhone SDK, very WYSIWYG. It still some kinks to work out with the beta iPhone SDK though.

I also like trying to reach the largest possible audience with the shortest toolchain. To me, this means favoring an HTML/CSS/JavaScript UI first, and only considering other alternatives when that approach will not work. Particularly since I favor developing applications for the internet.

One Mobile Renderer to Rule Them All
I did not do mobile development until TinyBuddy IM. It just seemed too painful -- too many custom environments, too many cumbersome toolchains for native apps. And the browsers really sucked.

However, now with WebKit on the iPhone and in Android, it is tolerable. Those platforms also have a decent toolchain for "native" apps: iPhone SDK/Objective C for iPhone, and a Java environment for Android.

However, WebKit is most interesting to me: it is a modern, very capable browser that will be on two major mobile platforms. While JavaScript performance is still slow on mobile browsers (both compared to desktop browsers and native code), there is promise that it will get faster with WebKit's SquirrelFish.

For the first time, it is actually possible to consider making a powerful web app for a mobile device. You have good odds of developing one mobile app that runs on multiple devices too. Or at least have lots of code overlap.

Of course a web app is not appropriate for every app, and it does not have access to as many services as a native app, but it does have a lot. With the latest WebKit for instance, you will have local storage.

You Are Dead to Me
Not every mobile device has WebKit. What about Windows Mobile devices, BlackBerry, or all the other devices out there in the market today? Well, for me, they do not make it easy to develop. And I am lazy. I use an iPhone, so I am going to develop for what I use. However, I chose an iPhone specifically because it is easy to do development.

So the other platforms are not that important to me, and if my developer laziness is any indication, they are in for some trouble. They need to get WebKit or Gecko on their devices and use a better toolchain. Opera's browser may be an option too, but I am not clear if it is up to the capabilities of WebKit. If it is, great. I like the Opera on the Wii.

Again, it may not be reasonable to discard these other platforms, depending on your app, but I have decided the other platforms are not worth the effort, given my interests. I really want to make interesting end user products without spending time on platform minutia, and I feel like the iPhone's and Android's reach will be good enough for me.

Mobile Application Design
I approach the technical design of a mobile app by trying to see if the following approaches fit the problem:
  1. A purely browser-based web app.
  2. A native app that gives a "desktop" icon, and access to phone services (for instance, location services), but use a WebKit view for the main UI interface. Set up hooks in the WebKit view so it has access to the phone services.
  3. A purely native application.
#2 has many shades to it, but I like it because it minimizes installed code. Revving a native application is more work than a server-based one.

Remember, I'm assuming this is an internet app, and you know the HTML/CSS/JavaScript trifecta already.

There are tradeoffs. Speed of loading/dependence on network, but again, I prefer to first look at a web-based solution first: maybe it makes sense for the native app to cache the HTML/JS/CSS/images locally.

Native Apps
What you want to build may not work as a web app. For those cases I still prefer iPhone or Android.

For iPhone, you have a nice dev environment with a WYSIWYG UI tool (Interface Builder). Objective C feels a lot like JavaScript to me -- some concept of a prototypical object, and runtime, dynamic dispatch where you can send messages to objects that may or may not respond to that type of message. Unfortunately you have to do the memory retain/release thing, but it is manageable. Lots of helpful videos and examples at the iPhone Developer site too.

For Android, using Java syntax and libraries are a big plus over some sort of C/C++ thing. Java can be verbose, but it is easy to program with an IDE, and hopefully they kept in the garbage collection like Java. That alone is a big win. I have not used Android yet, and I have heard of problems with trying to program in that environment (maybe they have been worked out now?), but it seems worth spending effort on that path. It seems like it has a good shot of survival over the long run.

UI Design
There is a great intro video on the iPhone Developer site about "User Interface Design for iPhone Applications". You will need a (free) Apple Developer ID to view it. But take the time to view it.

The main point: a mobile app is not a desktop app. Do not try to do too much. For TinyBuddy IM, I decided to not include buddy list management features in the UI (add/remove/move buddy). The app was designed for doing quick IMs with your buddies. Buddy list management just seemed to get in the way of that goal. Not to mention the lack of copy and paste on the phone. It just did not seem like a worthwhile activity for a mobile app.

Also, use the UI paradigms available on the phone. For native apps, Apple has a lot of built in templates for using the standard lists and tables you see throughout the iPhone UI. I used a very early version of iUI for a web toolkit that gives an approximation of the native iPhone UI. There may even be some resources in the latest iPhone SDK to make web development easier in this respect.

So, keep it simple, keep it familiar. Once you get the fundamentals down and released something, you can consider your own style (if appropriate).

Pain Points
It is not all roses. There are notable restrictions making mobile web apps. These limitations are specifically from the iPhone, but I would expect them to be roughly the same if not more oppressive in Android environments.
  • Resource limits.
  • Cache limitations (25KB max size, uncompressed, if you want it cached, 19 total cacheable items).
  • As mentioned above, JavaScript performs slower than desktop JavaScript or native code.
  • You web app is paused when it is not in the forefront (also a limit for native apps, but native apps will have a notification service you can use that will help with this).
That last one is particularly vexing for TinyBuddy IM, since it is an IM client. So a native app for the IM problem space is probably more appropriate. Or some sort of native component that ties into to the notification service? :)

TinyBuddy IM Weaknesses
I need to spend more time optimizing the TinyBuddy IM code:
  • I do not do any sort of "Fetch next 25" sort of thing to keep the buddy list size down.
  • The wiping between screens is slow.
  • The IM conversation window needs more polish.
  • Maybe some better navigation for multiple IM conversations.
So I definitely did not get it right with TinyBuddy IM. Now that there is a native IM app under development, I probably will not rev TinyBuddy IM again. However, I am amazed TinyBuddy IM works as well as it does -- I was able to be a little bit lazy, and still get something somewhat useful out. A good indicator for that platform.

Authentication
Most things I want to build require user authentication for at least some part of it. For TinyBuddy IM, that meant using AOL's OpenAuth authentication, since the Web AIM API used it. OpenAuth has a browser-based authentication system that any web page can use, and it even supports consuming some validated OpenID providers.

OpenAuth has a "clientLogin" API for desktop clients and Flash/AIR apps but I do not prefer it since it basically means the user hands over their name and password to your app, and then your app sends it to OpenAuth. While that might be nice from a usability standpoint, it bothers me personally from a security standpoint.

This is where something like OpenAuth's browser API (and in general OpenID in the browser) provides better protection. You only enter your name/password on the identity provider's web site, and a token is passed back to the web app that requires your credentials.

As an end user of this system, I can verify by the browser URL who is asking for my name/password. As a developer, I like just not having to touch passwords at all. It absolves me from the security issues with trying to manage passwords, even if it seems like I am only passing the password through to OpenAuth's clientLogin.

However, most end users are not this particular about using applications, they are happy to give their password to any site asking for it, particularly if it seems to be done in goodwill. They just want to use the app. Also, the browser based authentication can be weird -- popping a new window to get the auth credentials.

But the OpenID approach is the way of the future (going to another site to authenticate), so might as well get used to it. Particularly now that the OpenAuth authentication page has a "Remember Me" option, so the user does not have to keep entering name/password on every visit.

Have Fun!
Enough of my ramblings. The big thing for me: mobile development can be fun, particularly with the iPhone platform. Android, while not at the same level as iPhone development yet, also seems like it has promise too.

Friday, March 28, 2008

The Beauty of Dojo 1.1

The Dojo JavaScript Toolkit version 1.1 is available:
There are lots of goodies in this release (see the release notes for full details), but here are some of the new features that resonate with me. I am primarily a Dojo Core user, not much Dijit or Dojox. For other perspectives on the new features, see the 1.1 blog announcement, and Shane O'Sullivan's blog post.

Multiversion Support
One of the slicker features (IMO, since I added it) is multiversion support: you can now run Dojo 1.1 with other versions of Dojo in the page without conflicting. You can also choose to rename dojo, dijit and dojox to other names. As proof:
The multiversion support is great if you want to provide your own JS library, but use Dojo underneath. What is even cooler: multiversion support works from the CDN (see the source for the examples above)! So if you are thinking of migrating from an older Dojo version, you can experiment with Dojo 1.1 in a new namespace without having to download 1.1.

Load Dojo after page load
Dojo 1.1 can can be loaded after page load (after the window.onload event fires) by setting djConfig.afterOnLoad to true. This makes the initial render cost for using Dojo near zero, and it plays nice if you want to do extreme progressive enhancement. Use the new djConfig option in conjunction with djConfig.require, to load dojo along with the modules you needed dojo.required after dojo loads. See the the demo page for an example.

Adobe AIR support
Dojo now provides strong support for AIR in addition to Dojo's existing integration with Google Gears via dojox.offline.

Client-side data storage via dojox.storage
If you want client side data storage, dojox.storage gives you a few options, and auto-detects the best one. dojox.storage has been updated to allow for using Dojo Gears, HTML 5 DOM storage, Flash or AIR DB storage.

Build system CSS optimizations
The build system will now inline @import calls that are in .css files in addition to stripping comments and whitespace in .css files. See the New Build Options section.

Try it out!
If you would like to try Dojo, but are not sure how to start, you can always start small: If you just use Dojo Base (one file, dojo.xd.js, from the CDN), you get a solid JavaScript base that makes building progressively enhanced sites much more enjoyable. A rough run-down of the Dojo Base functionality can be found here. That article is for Dojo 0.9, but it still applies for 1.1. Dojo 1.1 is slightly larger than the numbers quoted for 0.9: The 1.1 dojo.xd.js is 29KB gzipped. See the 1.1 release notes for the new features that account for the size increase.

I prefer to stick with Dojo Base, with some additions from the Core modules (like dojo.io.script, which allows using JSONP APIs, like the ones provided by Web AIM). This is how I approached development for the AIM Chat web site and for the iPhone IM web app TinyBuddy IM. But given Dojo's depth, I was able to leverage the Dijit widgets to create a simple admin site for AIM Chat by adding in a few more dojo.require calls. Sweet!

I am really amazed at the amount of work that has gone into Dojo 1.1. The more I use it, the more I feel it is the complete JavaScript toolkit solution. You can do the small, quick progressively enhanced web sites using just Dojo Base from the AOL CDN, but scale all the way up to very rich experiences that use the full power of widgets, awesome data stores, offline storage, and incredible 2-D drawing capabilities (with charting!).

I feel the Dojo community is really hitting their stride now. Great job everyone!

Thursday, January 31, 2008

Browser and Dojo updates on Fragment ID messaging

Some browsers have changed how they deal with fragment ID messaging:

Safari 3 throws an error if a child frame tries to change a parent frame's location, but only if the parent frame is not the top-most frame. So, this test works, but this one fails. Parent frames are allowed to change a child frame's location.

Opera 9.25 has locked down cross-frame access to some frame properties. As Julien LeComte pointed out, Opera supports the HTML5 cross-document messaging API, so that is a viable alternative.

I have updated dojo.io.proxy so that it now works in these browsers: IE 6 and 7, Firefox 2 and 3beta2, Safari 3 and Opera 9.25. You can get it from the Dojo development trunk, or wait for Dojo 1.1. The new dojo.io.proxy code works with Dojo 1.0, and there are no API or usage changes.

This new code also improves the IE7 codepath: before the code used to use 3 frames to do the transport, but now it only uses 2 frames, like the rest of the browsers. I was not being very smart about the ordering of the frames in the old code.

Thanks go to Julien LeComte for pointing out the Opera cross-document API and for diagramming another way to do fragment ID messaging.

dojo.io.proxy differs from Julien's CrossFrame approach:
  • it allows very large messaging (it supports fragment ID chunking)
  • it is focused on providing a data transport -- it provides an XMLHttpRequest facade. It is not a communication transport for UI widgets.
Julien did not want to use location polling, since it increases CPU usage. dojo.io.proxy does use polling to do the communication (this allows for chunking to support large messages), but polling is active only while the message is in transport. I consider the window.location polling less resource-intensive than doing a DOM animation. CPU usage could be tuned by slightly extending polling interval if it is a concern.

dojo.io.proxy creates the frames only when the request starts, then it destroys the frames when the response finishes. This preserves the history stack in all browsers except Opera.

There is a very primitive test page if you want to see the latest code in action.

Wednesday, January 30, 2008

David Baron's solution for X-UA-Compatible

I like David Baron's alternative to X-UA-Compatible: if the main problem is intranets, give intranet folks a way to configure IE to use the old rendering mode just for their hosts.

I'm interested if this would be enough for Microsoft. If they claim it does not cover all the cases, it would be good to get some sort of metric/percentage of how much it would cover. Seems like it would cover the vast majority case. Probably close to 100% of the cases where Microsoft's revenue is impacted.

If the claim is that they cannot "break the web" even for that small case on the internet where their revenue is not directly impacted, I wonder how important those sites are anyway. I expect there are lots of sites that were made with older browsers in mind, but the site is probably abandoned too. As long as you can sort of read the content on the page, maintaining precise layout is probably not important.

If Microsoft is still set on using a flag/switch in HTML/HTTP header, I still prefer a cultural flag and not a product version flag.

Saturday, January 26, 2008

Using Aptana Jaxer for search engine friendly pages?

I am interested in Aptana's Jaxer because they seemed to have gotten Gecko to work on the server. From what I have read before, the tricky part is getting Gecko to run without the need for a windowing service (like X-Windows). Maybe Jaxer still needs a windowing service, I have not looked that closely.

I am not so interested in using their runat="server" or server-proxy capabilities, but more interested in getting Jaxer to render my page to the final "onloaded" DOM that could then be sent to a search engine.

However, I have not seen an option yet to tell it "treat the whole page as runat server" or something like that.

Here is what I want to do (condensed from my previous post on searchable ajax):
  • Do the model/view/controller in HTML/CSS/JS. I normally want this all to run in the user's browser.
  • However, for search engine requests, I want to do some Apache rewrite/proxying to a server-side Gecko that would render the DOM as it would look after window.onload, then serialize that DOM as the result of the search engine's request.
I wonder if Jaxer could be used for the "server-side Gecko" part. If so, that would be sweet. That would allow my web app to be indexed by search engines properly, but still give me the development model I want. No special dev work to get search engine benefits.

Wednesday, January 23, 2008

X-Web-Epoch instead of X-UA-Compatible

This is in response to the X-UA-Compatible proposal from Microsoft to help IE8 and future IE browsers to know how to render web pages. There is a lot of commentary on the subject, but here are what I believe to be the original (public) sources for the proposal:
I understand and sympathize with IE's position: they need a switch to know when they can use a more standards-compliant rendering engine. There is a vast sea of font tags, tables, spacer images and document.all references inside corporate intranets, and no one is going to update that code, ever. However, any new versions of IE are expected to be able to render that sea of forgotten markup faithful to the original coder's intent. A responsibility IE gets for winning the first round of browser wars.

However, IE is getting push from today's web developers that want something easier to code against given today's suggested cultural approach to web development, namely adherence to publicly accepted web standards.

X-UA-Compatible provides a switch for IE to know what the developer wants: does the developer want the old wild west behavior, or a more standards-based behavior?

However, I do not like the X-UA-Compatible proposal because it focuses too much on specifying a rendering engine and engine version (like specifying IE=8). Actually it is even more specific than rendering engine, it is really a product version switch.

I think the switch should be more of a "culture version" switch than an explicit product version switch. Tying the switch to product versioning seems too fragile. Ideally, IE8 and IE9 will be released before there is a cultural shift in expectations over development.

It also makes it hard for me as a more standards-based developer to make a decision on X-UA-Compatible. I'll likely just use the "edge" value, but then over time, "edge" will lose its meaning and it will just mean "what web developers do today". We'll need a new switch in the future once "edge" reaches the status of today's DOCTYPE.

Maybe it makes more sense to use a culture version number instead. For the sake of argument, I'll call it X-Web-Epoch, but I am not tied to that name. The value for X-Web-Epoch would be an URL that corresponds to the epoch that is targeted. I see having two epochs right now:
  • Epoch 1: The tag soup, wild west version of the web IE is expected to render, in particular inside corporate intranets.
  • Epoch 2: The standards-based development of today. I would also include "the expectation that things develop and change over time" as part of that Epoch 2.
Epoch 1 is the default. It is used for web pages that do not have a X-Web-Epoch META tag or HTTP header.

So, something like this for Epoch 2:

<meta equiv="X-Web-Epoch" content="http://www.w3.org/epochs/2">

That www.w3.org URL does not actually exist, so spare the w3.org servers and do not try to actually go to it. This does not have to be the actual URL mentioned above, just some URL.

The URL should be resolvable to a real document that explains the cultural assumptions of that epoch.

The Epoch 2 document could list things like:
  • Assumes public standards-based development.
  • List the general set of standards.
  • Maybe list things like progressive enhancement, how to design for accessibility.
  • Most importantly: things will change over time as bugs are fixed to improve standards compliance. There also may be new standards adopted.
  • It could have a changelog since this document is allowed to change slightly over time.
The document would hopefully set expectations accordingly, in particular that things may change slightly over time. Bonus points: the document can serve as a pointer to how best to code in that Epoch.

Ideally the definition of Epoch 2 is big enough that we might have a chance to go many browser versions without needing a new Epoch, and browsers can go a long time without needing completely new rendering engines.

In reality, cultural expectations will change over time, and we will likely need an Epoch 3 to allow the browser market leader of that time to be able to implement a new rendering engine, so they do not have spaghetti code in just one giant backwards-compatible engine. But again, there should be a longer time span vs. what might be expected with X-UA-Compatible.

It also seems like a better approach than using "edge" in X-UA-Compatible.

This would give IE the switch it needs for its new rendering engine. Other browsers of today, they are just fine moving along with what they have now. They can ignore the switch for now, until they become market leader and have to adapt to an Epoch 3. Hopefully, web developers get iteration from IE in a more timely manner, and a better baseline for development.

Monday, December 03, 2007

Defining $ for Dojo and setting a function prototype

This is a little story about defining a $ function for Dojo, and a more fundamental discussion about setting a prototype for a function.

Defining a $ function for Dojo

I generally prefer using full namespace names when using JavaScript libraries. It helps avoid confusion when passing around code or copying/pasting code that may be used in mixed-library environments. However, if you have contained environment, you may prefer extreme brevity over namespace robustness.

So, how can we define a function named $ that works with the base Dojo functionality (the stuff you get in dojo.js: DOM querying, event handling, XHR, style, basic effects, JSON, array, language and object hierarchy helpers). Ideally, $ would be mapped to dojo.query, to allow easy node selection, but then any other dojo method, like dojo.connect() would be available via $.connect().

Here is something that works:

$ = function(){
return dojo.query.apply(dojo, arguments);
};
dojo.mixin($, dojo);


That does the basics: defines a function $ that returns the result of a dojo.query() call, then copies all the properties of dojo to the $ function object (via dojo.mixin()).

This allows for making these sorts of calls:

//Set on onclick listener for all divs in the body
$("div").onclick(function(){
alert("div clicked.");
});

//Do an XMLHttpRequest POST using data from
//a form with a DOM ID of "myFormId"
$.xhrPost({
form: $("#myFormId")[0]
});


Setting a prototype for a function

So that is nice if all I want is dojo base. However, Dojo has a neat module loader, and ideally I want to be able to do something like:

dojo.require("dojo.io.script");


Then later in the code just do a $.io.script.get(). However, this will not work if you define the $ function before doing the dojo.require() call. dojo.mixin() only copies over object properties that exist at the time of the dojo.mixin() call.

So, ideally, I want to set the prototype lookup for $ to be dojo. That way, as new things are added to the dojo object, they will be automatically available via $.

At this point I hit a wall, at least with my current understanding of JavaScript. I was hoping I could do something like this:

var Dollar = function(){
return function(){
return dojo.query.apply(dojo, arguments);
};
};
Dollar.prototype = dojo;

$ = new Dollar();


In JavaScript constructor functions, you do not have to return a value. If you do not, whatever the "this" value was inside the constructor function gets returned as the result of the call the "new" call.

However, if you return a value from the constructor, that is what is returned as the result of the "new" call. This is what Dollar does above.

What I was hoping would happen is that the returned function (that calls dojo.query()) would have its __proto__ property set to Dojo, but I think the __proto__ binding happens before the constructor function is called, so that you can access methods on "this" inside the constructor.

Suppose there was a constructor function called ConstructorFunction, and you called "new ConstructorFunction()". Here is some very crude psuedo-code for what I think the "new" call is doing:

var temp = {};
temp.__proto__ = ConstructorFunction.prototype;
return ConstructorFunction.apply(temp, arguments);


If this is what is going on, it makes sense that by returning a value from the constructor function call, I lose out on the __proto__ binding.

So why not just set the $.__proto__ value directly after $ is defined? Accessing __proto__ is strongly discouraged. It is an implementation's private implementation detail, subject to change. Also, doing that only works right now for Firefox 2 and Safari 3 (failed for IE 6 and Opera 9.24). So stop thinking about it.

I also tried something like this:

Function.prototype = dojo;
$ = new Function("return dojo.query.apply(dojo, arguments);");


but that fails for what I believe to be similar reasons as my first prototype attempt.

I am very interested if someone can suggest how I can modify the $ function's prototype lookup, so that it uses the dojo object. It is neat to think about defining an object that also supports the () operator on it (with a custom prototype).

I want something that works with today's browsers, not ECMAScript 4. Although seeing an ECMAScript 4 implementation might be instructional for the future.

Saturday, July 28, 2007

Ajax Experience Talk about Dojo XD Loader

I attended the Ajax Experience West conference this week, and gave a talk about Dojo's XD loader. It was my first time giving a talk in that venue. I got some good feedback from Naveed (from the dev.aol.com group): I was talking a bit too fast, and didn't make enough eye contact, focusing too much on the slides. The content was good, but I could improve on the delivery. I was suspecting as much, but it was great to get the good feedback. Apparently I'll get feedback (hopefully) from the talk evaluation sheets.

It was neat talking with one of the jQuery contributors, Yehuda Katz, who has been thinking about how to handle serving jQuery plugins. They have been considering a way to load plugins, and possibly specify dependencies. They may not need the full xd dependency mapping, but it would be neat to share some ideas in that area. In the talk, I talked about trying to modify other toolkit code from YUI, Ext and jQuery plugins to load via the dojo loader. I still might try that as an experiment.

In the presentation, I talked a bit about xd loading in general, and to address the security concerns of how to verify the code you are xd loading has not been corrupted or changed. I was thinking using digests of some kind would be good. Ideally, the browser could do it before it evaluated the JS code.

I mentioned the idea to Douglas Crockford, and he had nice idea of being able to specify more than one URL for the script, to serve as backups in case there was a failure with one of the first one failed. He thought it would make a good browser plugin. The multiple URL thing also came up as a suggestion in the Q&A part of the talk, but in the context of dojo.registerModulePath().

Initially, part of the Q&A discussion was ways to do the digest checking without needing a browser plugin, but the more I think about it, it really needs to happen by the browser, since we need to do the check before the imported script is evaluated in any way. And if we are doing xd-loading of the script, that means (at least today) that a script tag is going to be used. So we need to extend the functionality of the script tag.

Looking around at the web, it seems like (as usual) these ideas have been in the air before. I went to Andreas Andreou's cross-site js sharing post, which pointed me to this moz.dev.platform group discussion. This mozilla bug was referenced, and from there, I went to Gervase Markham's Link Fingerprints page.

The Link Fingerprints seems like a workable system. Most of the discussion has been focused on sharing of library code, but it also seems to dovetail nicely with a security aspect. I might ask the moz.dev.platform group about the status of the mozilla bug and mention my desire to have it for security concerns. I'll also ask about supporting alternate backup URLs for download. Maybe as nested script tags?

If this works out, I would feel much more comfortable strongly suggesting xd loading for folks. It would result in a safer web. Very nice.

Saturday, July 07, 2007

TinyBuddy IM: Instant Messaging for iPhone

(Update 7/15/2007: I've moved the TinyBuddy IM-related info over to another blog: Tiny Notes)

Have an iPhone, but wish you could IM your buddies? Now you can, with TinyBuddy IM. It is an AIM® Enabled web-based IM tool. It works by using the Web AIM API, OpenAuth and Dojo.

The nice thing about this solution: you do not send your AIM password to me -- you are redirected to AOL's OpenAuth servers for authentication. My JavaScript only sees an auth token. Furthermore, my web page has to get your explicit consent before accessing your buddy list data and before sending the first IM or presence change.

So the price of this added security is pop-up windows. A new window will be popped so you can authenticate with the OpenAuth servers, and also when giving consent to the application to access your buddy list and IM. For the consent prompts, you can choose "Grant Always" to avoid them on subsequent logins. I think the pop-ups are worth the added security, and at least in iPhone's Safari, window popping looks neat. Unfortunately, the OpenAuth Sign In and Consent pages are made for larger windows, so you will have to double-tap zoom to read them.

Another neat feature of this web application: it is pure JavaScript, HTML and CSS. No server-side languages needed. Dojo really made it easy to do this. I used Dojo 0.4.3 because I want to reuse this code for some other projects that are on 0.4.3, but if/when I get enough time, I would like to port it over to the 0.9 code.

So give it a whirl if you like. I'm sure the code it not bulletproof, and I've noticed enough weirdness with iPhone's Safari to guarantee that I will not be able to give comprehensive support. Also, even though I'm an AOL employee, AOL does not endorse this project or have anything to do with it (but thanks to my co-workers for ideas and early testing, all done of their own accord).

Also, I'm using an OpenAuth dev key, so if there is too much usage you might see some rate limit errors, but we'll see how it goes.

Some other interesting tidbits:
  • The Web AIM API is a Comet API. It uses long polling to work cross-domain in the browser. I'm using more of a short poll with pauses between the polls to hopefully smooth out network hiccups on the phone.
  • Don't like the CSS? You can make your own and tell the app to use it instead. Go to the test launcher page to specify the path to your CSS. Click the Launch button, then copy launch URL. Use that URL when you want to use the application. This feature is not allowed for IE browsers given its security problems with CSS "expressions".
  • I'm serving the code gzipped. The HTML, CSS and JavaScript combined come to about 90 KB. So it is tolerable on the EDGE network.
  • Use the iPhone two finger scroll to scroll the buddy list and IM conversations.
  • Typing IMs should be optimally sized for use with the virtual keyboard. Just type in the text box at the bottom of the IM window and press "Go" on the keyboard.
  • I'm using Dojo Accordions for the IMs and buddy list. I like the use of space with that model and that I can show you incoming IM text if that IM AccordionPane is in the closed position.
  • onbeforeunload does not seem to fire for iPhone Safari. That makes it hard to log out correctly, so to clear your OpenAuth cookies, be sure to use the Available, Sign Out menu item.
To use TinyBuddy IM, just type http://tybyim.com in the iPhone Safari browser. You can try it in other browsers, but it looks best in the iPhone Safari.

Friday, May 25, 2007

Searchable Ajax with JavaScript controllers and a headless Gecko

I'm back from a much appreciated vacation, and just wanted to jot down some thoughts that I want to expand on later. This is a continuation of how I want to build web applications on the RIA-end of the web scale, but an approach I could use for just about any data-driven web site.

I only want to use the server to serve files and to implement data storage. There should be no server-side work dealing with the UI controller. This means the "application server" part of the server (the PHP, Java, .Net, RoR part) should only be handling the API to save/modify/get the data. It should not be doing any presentation or UI flow. JavaScript, CSS and HTML should be used for the presentation and UI flow.

So, the app server implements the data API as a REST API. This gives a good boundary of what should be done in the app server. The data should use XML (preferrably ATOM) or JSON as the data markup. The only presentation part might be to apply something like an XSLT transform of the data if the GET request matches a browser or searchbot user agent. Alternatively, use the domain name of the GET request to know whether or not to apply the XSLT transform (sometimes it is nice to host the pure data API on a different domain for security and load reasons).

Progressive Enhancement should be used for adding the JavaScript controller to the page, so that hyperlinks to other resources can be followed by searchbots or non-JS enabled browsers. Editing of the resources via an HTML interface could be restricted to JavaScript-enabled browsers (mostly where a UI controller is needed anyway, and search bots only care about indexing GET resources anyway).

So that is a baseline. This baseline now cleanly separates the UI controller from the server side, and makes it less relevant what sort of app server technology you use. The UI controller is kept in the JavaScript realm, no more splitting it between JavaScript and [Struts, RoR, Django, etc...].

Now it is time to jump the shark:

Restrict the data API to its own domain, separate from the domain used for presenting the UI for the browser/search bots. For example, use api.domain.com for the data API and ui.domain.com for the browser/search bot interface.

The HTML files on ui.domain.com use Ajax-like techniques access the resources on api.domain.com to show the UI. The Ajax-based data loading would have to be a cross-domain ajax. If the API and UI domains share a common sub-domain, document.domain tricks could be used. Otherwise (and more interesting to me personally), JSONP or XMLHttpRequest IFrame Proxy could be used.

A nice benefit of this approach is that all of the UI pieces (HTML, JavaScript and CSS) are highly cacheable and servable from very different domains than the API domain.

So what about search bots? They cannot do the Ajax techniques, at least not yet. For them, in the web server config, route their user agents to a web server module that uses Gecko to fetch the page, and do the ajax loading. The HTML can send an event to Gecko (this custom, headless gecko can export a function, something like window.onAjaxFinished()) to tell it when it is done rendering. Then the Gecko module serializes the current state of the DOM and sends that HTML string back to the search bot. It could even keep a local cache if it liked, if the resources did not change that often.

This approach would allow the search bot to get a good representation of the page for their indexing, and since Progressive Enhancement was used to bind to resource hyperlinks in the document, the search bot can still navigate to other resources on the domain (and those requests would be funneled through the web server's headless Gecko module).

In this model, REST via HTTP becomes the new JDBC. And more importantly to someone like me who enjoys the front-end -- I don't need to learn about any of the app server flavors of the month to implement data access (the other app server-hugging developers on my team can do that).

So now I just need to do a custom build of a headless Gecko that I can use in a web server module. Any pointers are appreciated. Ideally someone should do this as an open source project so everyone can benefit.

Tuesday, October 24, 2006

IE 7 and IFrame APIs Part 2

Microsoft was kind enough to look into the issue, and here is more information.

The "Navigate sub-frames across different domains" preference is designed to address this security report:
http://secunia.com/advisories/11978/

IE 7 has shipped with automatically turning this preference on.

There is a workaround to the issue: the two iframes that do the communication must both have a parent frame from their domain loaded.

So, the original setup I use in the XHR IFrame Proxy situation looks like the following. The dashes indicate the level of nestedness (Top Window contains IFrame 1, IFrame 1 contains IFrame 2):

Top window (Domain A)
--IFrame 1 (Domain A)
----IFrame 2 (Domain B)

Where IFrame 1 and IFrame 2 communicate by setting each other's fragment identifiers (location.hash in JavaScript). When the communication is done, IFrame 1 tells the top window what the result is.

What broke was IFrame 2 (on Domain B) setting IFrame 1's location (via parent.location).

The following iframe structure works in IE7:

Top window (Domain A)
-- IFrame W (Domain B)
---- IFrame 1 (Domain A)
------ IFrame 2 (Domain B)

Notice the introduction of IFrame W. Now, apparently since IFrame W (on Domain B) owns IFrame 1, it is OK for IFrame 2 to change the location of IFrame 1.

Here is a test of this structure (thanks in large part of Microsoft).

This test uses something I personally have not done before: using window.open() to grab named iframes. Interesting technique. I'm curious to see if it is required when I patch IFrame XHR for IE 7.

So, the end result is that *another* IFrame is needed to accomplish the task. I'll be looking at modifying XHR IFrame Proxy code to use this extra frame, but only for IE 7. The down side is that now, in addition to the remote server placing xip_server.html on their server, they will have to place another HTML file too (the file for IFrame W). If the code bloat is not too much, I might consider making xip_server.html smart enough to act as IFrame W content as well as being the content for IFrame 2.

So, while it works, I still ask Microsoft to consider the following:

1) It seems like this change to address the security issue (http://secunia.com/advisories/11978/) seems like it should not affect setting parent/child frames. Firefox seems to have addressed the security issue without breaking subframe communication. I know that is not entirely a fair comparison, but it seems like the code change in IE to protect against a specific issue has too broad of an effect.

2) Even with this preference on, allow changing of fragment identifiers. It is a non-destructive change to the parent frame, and the parent frame can choose to ignore it. It is also a very solid, useful tool for cross-domain communication that works for today's browsers.
If you want to get fancy, consider supporting a security setting on the iframe, that allows Domain A to say it trusts content from Domain B to change its location. Ideally this sort of configuration of an iframe would be some sort of standardized access control scheme that other browser makers could implement if they so desired.

Wednesday, October 11, 2006

IE 7 Breaks IFrame APIs that use parent.location

Update (Nov. 7, 2006): Microsoft provided more information. See Part 2.

Original post (Oct. 11, 2006):

One of my co-workers was recently testing IE 7 RC1 and found that using fragment identifier messaging (FIM) with IFrames did not work. When the content in the nested IFrame tried to set parent.location, IE 7 popped a new window instead. If you have an IFrame-based API that uses parent.location, then you might be affected by this change.

I would like Microsoft to reconsider the preference change in IE 7 that causes this issue, and this post describes why. Feel free to contact Microsoft if you feel likewise. More information below.

When I investigated the issue more, I found that IE 7 only popped a new window if there was a nested IFrame. If there was just an IFrame in the top level document/browser window, it did not pop a window.
We contacted Microsoft through our company's Microsoft channel, and found out the following:
  • There is a preference that was introduced in Windows XP SP2 that controls this behavior. You can find it by looking in the Internet Settings -> Security Tab -> Click on Custom Level button -> "Navigate sub-frames across different domains".
  • With IE 6, this is set to "Enable" by default.
  • When IE 7 installs, IE 7 automatically sets the preference to "Disable" by default.
I would prefer that they do not change the preference for the following reasons:
  1. There is no documentation about the change, and the change was done without checking with the web developer community.
  2. I think the behavior is buggy and could use refinement.
  3. Fragment identifier messaging (FIM) across IFrames is the best thing we have for allowing pure browser, cross-domain web APIs.
If you feel the same, or if you feel that your own IFrame-based web API that sets parent.location might be affected, then send feedback to Microsoft. I will try to find out where we can send feedback and update this post when I have the info.

More information on why I think IE 7 should keep the preference as "Enabled":

No public documentation of discussion

Our Microsoft contact did not provide any public info indicating this was documented or discussed publicly. When I asked for a description of the change, there was no documentation that was ready for public consumption yet. There might have been some public disclosure about it, but I am not aware of any. I apologize if there was something posted. I have been subscribed to the IEBlog for a while now, but none of the previous posts triggered any warning bells for me. But I could very well have missed it.

This page describes that security preference, but reading does not seem to be very helpful (and seems to discuss talking to sub-frames and not parent frames).

I do not mean to chastise Microsoft on this point. I can appreciate with such a large project as a browser, it might not always be evident what changes can have an adverse effect on people using your product. And even if they did note it somewhere, there are lots of changes, and it could very easily get lost. It has happened to me on my software projects.

I can also see that their answer could be "why don't you just test with the IE 7 betas and release candidates? If there is an issue, let us know". That is a fair and valid position to take. But just as it might be hard to document and message all the changes for a large product like IE 7, it can be hard for web developers to stay on top of everything. I can see many not taking IE 7 testing seriously until RC 1.

And given that the parent.location works as long as you are communicating with a browser window and not an IFrame, I can see for some developers using IFrame APIs, there might not appear to be an issue.

For my case, with the XHR IFrame Proxy, I just worked out that solution July 31st. I haven't done IE 7 testing because I have been busy, and I thought the big things for that release were CSS-related changes (from a web developer POV). There did not seem to be that many JavaScript changes (besides an apparent increase in the JS engine performance). But still, I feel bad getting around to this issue this late in the IE 7 cycle.

So, ideally both of us could have caught the issue sooner and talked about it, but it is understandable that it did not happen. And I hope that since it is a preference change, that changing the IE 7 behavior back to "Enable" is hopefully an easy thing to do even this late in the IE 7 cycle. This would give more time to discuss the issue.

Behavior is buggy and could use refinement

I am sure there is a good use case to justify having this preference, and perhaps it even describes the current behavior. However, from my outsider's point of view, it seems buggy.

Why is Test Case 1 allowed (setting parent.location on the top browser window), but setting the parent.location on an IFrame is not allowed? They both seem to be "cross frame" communication, and violate whatever security issue the preference tries to address.

In addition to addressing the apparent bug with the behavior, I think setting parent.location should be allowed if the new value for parent.location is the same protocol, domain, port and path, but just differ by fragment identifier. In those cases, the parent document is not destroyed or modified, so perhaps whatever security issue is being addressed with this preference does not apply? Allowing fragment identifier changes is important for the next point...

FIM is the best thing we have for allowing pure browser, cross-domain web APIs

There are other ways to do cross-domain communication in a browser, but they all suffer from some limitation that does not make them a good general purpose, pure browser solution.

I also believe Fragement Identifier Messaging (FIM) that is used as part of the XHR IFrame Proxy is the most secure cross-domain communication option, since it requires explicit action for a server to allow the cross-domain action, and it has very fine grain control on the types
of requests that are allowed. FIM also does not automatically "execute" the response as using dynamic script tags might. The receiving frame can choose to just ignore messages if it wants.

And with the exception of the current behavior in IE 7 RC 1, FIM works in all other (major) browsers: IE 6, Firefox 1.5 and 2.0, Safari 2.0, and Opera 9.

The real solution would be a native XMLHttpRequest that can do cross-domain requests, but even when that becomes available, it will take a couple of years at the least to be widespread enough that it is actually be usable by web developers for cross-browser solutions.

In Conclusion

Microsoft, please do not change the preference to "Disable" with IE 7. Keep the default "Enable" behavior of IE 6. If at some point you want to change the preference to "Disable" by default, consider a public discourse of the implementation issues discussed here and the impact on FIM.

Wednesday, August 30, 2006

How I want to build web applications

Use a web server and browser technology (HTML, JavaScript, CSS, etc...) to build the application. Do not use an application server, or any kind of server that is more than a simple web server. Ideally, I could serve the application from the local file system and it would work.

Data service APIs should be usable by JavaScript running in the browser. The data services can be implemented in any choice of server, but the API should not indicate the server implementation. For instance, if I'm using a getItems service, it should not have getItems.jsp/getItems.php/getItems.aspx as part of the API URL.

The web application should be runnable from any domain on any web server. This means the data services must support some sort of cross-domain access from a web browser. My preferences:
The web application might need to access data services that require authentication. The authentication service should be able to support the cross-domain techniques mentioned above. IFP XHR is the more robust option, so it might be preferred when using services that require authentication.

I believe this approach gives the best overall value with scalability and perceived performance when compared to server cost. Scalable because the code can live on any web server, and well-performing because the entire application can be heavily cached by the browser. There is a first time load cost to fetch the files, but after that, the files should have very long cache times. This gives great perceived performance to the user -- things start to visibly happen in the browser window as soon as the user goes to the application URL.

There are solutions that might give you better scalability or better performance, but this approach gives the best overall value when considering server cost. Only simple web servers are needed, and with the high cacheability of the application, the web servers will not need to do work for every invocation of the application. The user's computer and browser are doing more work, but since many (most?) of the applications and services are free, or nearly free, I believe this is an acceptable tradeoff. Of course there are exceptions to this rule, in particular cell phone web applications. However, I do desktop/laptop web application development at the moment.

A very important point about this development approach -- *anyone* can run the web application. Every ISP gives you some amount of disk space on their servers to serve files. This development approach allows you to use that ISP space to run a web application. And I believe that is a key driver to allowing widespread "mashup" usage. Anyone with a text editor, access to the internet, access to cross-domain data service APIs, and a web browser can make a web application usable by the whole world. I am not talking about just serving some web pages, I mean serving web applications: email, instant messaging, calendar, a map service mashup. That is beautiful.

Tools I want to use

A web server
Preferably Apache, but any solid web server that allows for setting cache controls on files will do.

A solid JavaScript toolkit
I prefer Dojo, because I contribute to it, and it offers a nice breadth of libraries with an include system, dojo.require(), that allow you to do progressive optimizations on the code you use without having to recode a bunch of your JavaScript. Once the accessibility and internationalization support is fully integrated, it will be a hard toolkit to beat.

A non-server, i18n-friendly templating system
JSP, ASP, PHP and Rails enable some nice templating systems, but they all require server infrastructure. Tagneto is my attempt at a templating system that does not need server infrastructure. It requires a compile-time step by the developer, but the output is just plain HTML/JavaScript/CSS runnable from any web server (even local disk).

Dojo has some support for a type of templating via widgets and the i18n work going in now, but I don't feel it is as performant as something that you can do with a compile step. Most of the templating work (and in particular i18n string work) could be done once in a compile time step instead of downloading all the code to do the template transformation and doing the transformation every time the code is loaded.

The downside with a compile step, is that the developer needs to run a bunch of compiles while developing. One thing I would like to do is enable development using Dojo for the templating needs, but then apply Tagneto during a build process. Or expand Dojo to do the work as part of its build process. Or just use Tagneto, but allow using it from something like Jetty on the local computer, and have that do the transform on the fly. Once the developer is finished, run the compile step as part of the build to generate the final static files that can be served from a web server.

But for me, I'm fine with doing the compile step as part of the development process. At least for now, until I commit more time to solving the problem. I want to help get Dojo 0.4 out the door first.

Tuesday, June 06, 2006

Cross Domain Frame Communication with Fragment Identifiers (for Comet?)

A page is served from a different domain than the URL for an iframe in that page. Normally cross domain, cross frame communication is prohibited for security reasons. However, the two frames can communicate with each other by using fragment identifiers (the hash part of an URL, like http://some.domain.com/path/to/page.html#fragmentIdentifier).

Since fragment identifier changes don't reload the page, state can be maintained in each of the frames.

This could be used to allow cross domain usage of an API that uses Comet as its communication with the server. Or for UI that a third party wants to embed it, but still allow some stateful communication with the hosting page.

The limitations:
  • Communication is limited to the size of fragment identifiers. I'm not sure on the max size for all browsers, but I would think the same limitation on the size of GET URLs probably hold here too. So the max size for the full URL should probably be kept under around 1KB.
  • Using the iframe may cause issues with the back button.
Test page.

Monday, May 22, 2006

dojo.io.ScriptSrcIO and Dojo 0.2.2

dojo.io.ScriptSrcIO allows you to use dojo.io.bind() but use script tags instead, effectively giving you cross-domain data access (but the data returned as to be JavaScript).

It was introduced for Dojo 0.3, but here is a version of that IO transport that works with Dojo 0.2.2:

http://tagneto.org/dojo/0.2.2compat/ScriptSrcIO.js

In order for it to work with 0.2.2. the back button integration was commented out. Also, since this is not a package that is included with the Dojo 0.2.2 release, you can either place it in the src/io directory of your Dojo install, or if you don't have access to the dojo install, just do a script src tag for this file, right after the dojo.js script tag:

<script src="/path/to/your/dojo/dojo.js" type="text/javascript"></script>
<script src="/path/to/your/copy/of/ScriptSrcIO.js" type="text/javascript"></script>

Then you can use this transport as described in the test page:
http://archive.dojotoolkit.org/nightly/tests/io/test_ScriptSrcIO.html

When you upgrade to Dojo 0.3, be sure to remove the extra script tag if you have it (since ScriptSrcIO.js ships with 0.3).

Wednesday, March 29, 2006

JSONRequest, Part 2 (Cross Domain Policy for All)

As mentioned in part 1, instead of introducing a new request object, JSONRequest, I would prefer to fix the current available pathways. However, I can appreciate that it may be hard to do that. If JSONRequest does go forward, here are some suggestions. The goal of these suggestions are to get JSONRequest to behave in a safe, useful way that could be ported to the other pathways, like script src or XMLHTTPRequest, if it all seemed to work out (and maybe one day merge JSONRequest with XMLHTTPRequest into a generic request object).

The main goal of these suggestions are to protect legacy server systems that did not expect cross domain requests. These suggestions require a server to do explicit actions to allow cross domain behavior. This should address most of the security concerns while laying the groundwork for a general cross-domain request model for the browser.

These suggestions are only for cross-domain requests made with JSONRequest. XMLHTTPRequest's same origin behavior can be used for JSONRequests to the same origin server.

I do not have any particular attachment to the names of the attributes and headers used below. They are just used for illustration of the concept.

The suggestions:
  • Allow Cookies with a xdallow Attribute
  • Request Origin Header
  • Cross-Domain Policy for Pages
  • Allow GET requests to Allow Caching
Allow Cookies with a xdallow Attribute

To alleviate the concerns about data stealing and impacts on legacy systems, introduce a new attribute for cookies, xdallow. New server services could use this new attribute to indicate that it wants to allow cookies to be sent in JSONRequests that originate from a page that is outside of the server's domain. Like today's cookies, the cross domain page cannot actually see the value of these cookies, but if it makes a JSONRequest to a server that set these cookies, they would be sent (as long as the request conformed to the policy set by this new cookie attribute):

Using an example from the cookie spec, current cookies can look like this:
Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; domain=anvil.acme.com; expires=Wednesday, 09-Nov-99 23:12:40 GMT
The new attribute would look like this:
Set-Cookie: CUSTOMER=WILE_E_COYOTE; xdallow=XDALLOWVALUE; path=/; domain=anvil.acme.com; expires=Wednesday, 09-Nov-99 23:12:40 GMT
where XDALLOWVALUE could be:
  • xdallow=all - all pages are allowed to use a request to the origin server with this cookie.
  • xdallow=domain.one.com,domain.two.com - a list of comma-separated domains
  • xdallow=http://a.domain.com/with/path.html - a full URL to allow. If the server wanted to allow multiple domains, send multiple Set-Cookie headers with different xdallow values.
  • xdallow=/path/to/policy.xml - A path to the server's cross-domain cookie policy. It would be in XML format and could allow specifying a whitelist and black list of domains and paths that are allowed.
Request Origin Header

The spec uses a Domain header to specify the server that is making the request, and the value is derived from document.domain. I would prefer not to rely on document.domain, since it is a script property that can be changed to a limited degree.

I would prefer to use the page's real domain and include the path of the page making the request. Effectively like the Referer header. I would like to just use the Referer header, but I can see for cross-domain requests only, a new header name should be used. In particular, if these changes were ever merged with the XMLHTTPRequest capability, a header that cannot be modified by a setHeader command should be used. So, perhaps use a new header name like:
Request-Origin: http://some.domain.com/path/to/page/making/request.html
This header would even be sent for https requests. If this is problematic, then don't allow https pages to make cross domain requests.

Cross-Domain Policy for Pages

This is a mechanism for checking if a web page wants to allow cross domain requests via JSONRequest. This is to mitigate the case when the web page is compromised by malicious script. If JSONRequest is asked to make a cross-domain request, it first checks with the server that the page originates from for a xdallow policy file. This policy file could be similar to the one mentioned in the cross-domain cookies section.

If the example page is at the following URL:

http://some.domain.com/path/to/web/page.html

JSONRequest would check the following paths in this order:
  1. http://some.domain.com/path/to/web/.xdallow/page.html.xml
  2. http://some.domain.com/path/to/web/.xdallow/
  3. http://some.domain.com/.xdallow/
Allow GET requests to Allow Caching

The spec does not allow for caching of responses or GET requests. If the cookies are not allowed for JSONRequest, I believe the majority use case for JSONRequest will be for non-authenticated APIs, and a majority of those cases could benefit from cached responses. I don't believe POST requests can be effectively cached by browsers or proxy caches, so it is important in that respect to allow GET requests. Respecting cache headers would also be required of JSONRequest.

If all of the above recommendations are used, it helps protect GET requests. Legacy services would still be protected since they don't support JSON responses. As servers roll out support for JSONRequest, they will be aware of the above requirements to allow cross-domain usage.

In general, these additions force newer services to do explicit actions to allow the cross-domain requests and the additions give those services the information to make intelligent, secure decisions. Legacy systems are protected if they don't opt-in to these requirements. They would have very strong protection by just disallowing any request with the Request-Origin header.

Tuesday, March 14, 2006

JSONRequest Thoughts

This is feedback related to JSONRequest.

Specific detail comments

I'm curious why it is designed as a static method invocation and request ID vs. XMLHTTPRequest's instance-based approach. I would prefer to match XHR's instance-based approach, unless there is a good reason to avoid it. It would be good to match the XHR idiom as much as possible, since developers are familiar with it.

I also like the idea of request limiting on failure count. It would probably be good even for scenarios when it is not an attack, but the server is just having problems. No need to pound the server relentlessly.

General Problem Comments

In general, it feels too limiting, and I'm not sure the limits do much in the end to make it a viable alternative to just dynamically adding SCRIPT tags to the HTML head to load cross-domain scripts and/or JS data. With the dynamically added SCRIPTS, you get cookies sent to the domain, and have the option to get more than just a JSON object.

It is dangerous, but the point is it is possible to do it today. The only thing missing is being able to do a POST and getting a standard callback to know the script has loaded. I tried solving those things with the Dynamic Script Request (DSR) API.

Even though it can be dangerous, developers can use it today, and if it solves their problem, then they will use it. If it is possible to get browser makers to do some work in the area of cross domain script usage, I would prefer to see that effort used to strengthen the existing method and to provide the ability to specify a page-level script policy concerning cross domain scripts.

Strengthen Existing Pathway

I would like to see the following things added to to script tags, and/or have a ScriptRequest object to encapsulate the behavior (I don't really care about the names of these attributes, just what capabilities they allow):
  • method="POST"
  • data="url encoded data sent as the POST body"
  • sync="true" (to get blocking requests)
  • onload="event callback that really worked and was called when the script was loaded"
  • createScope="true" (or something that says "don't treat read the data and do the equivalent of a window.eval () on it. Instead, treat the data as specific scope or as instance object).
Maybe instead of onload, onreadystate, to allow Comet-style connections (the "Duplex" section in the JSONRequest document)?

The HTTP request headers could not be modified by the script request object (except indirectly, in the case of the cookie header). In addition, I would require that a Referer HTTP header be sent with the request. There was a comment on the WHATWG mailing list that Referer is not sent for HTTPS requests. I think something needs to be sent to indicate the calling domain and page path in the HTTP headers so that the server handling the request can make a decision on whether to allow the request.

Page Level Cross Domain Script Policy

Cross domain XML policy files on the server (like the ones used by Flash) do not seem sufficient. As far as I know, those only allow setting domain-to-domain trusts, but there could be many types of web applications on a domain (one HTML page can be a whole application). Sites can host pages from many users.

It should be possible to specify a cross domain script policy in a web page. Something that would set the permissions for scripts that come from other domains than the current domain. It could even be used for any exterior scripts that are loaded, even ones on the same domain. I'm not too particular on that, but it might be a good idea.

The policy would be specified by an HTML element, a child of HEAD. I do not care what the tag is called, but for the purposes of illustration, I'll use a META tag. But again, I do not have any special feelings about using META:

<meta name="ExternalScriptPolicy" content="dom=yes,cookie=no,request=no">

Different script capabilities can be allowed/disallowed, like whether to allow:
  • DOM access
  • Cookie access (maybe break it out by read and write)
  • Request access (whether the external script can use XMLHTTPRequest or ScriptRequest)
  • Other restrictions/privileges?
This tag could not be modified by script.

Conclusion

JSONRequest has some nice ideas, but since there is a gaping hole with SCRIPT tags anyway, I would prefer to use the browser makers time to improve the SCRIPT pathway, make it better to identify request origins, and provide page-level script security policies.

Wednesday, January 25, 2006

Clarifying Tagneto's mission

I think the description of Tagneto in the Why and What documents may be a little vague or too generic. I'm trying to clarify the mission and the message of the mission. Here goes:

Provide tools to build fast, rich web applications that don't require application server infrastructure.

Creating rich user interfaces in HTML should not require a server infrastructure. A web server is needed to serve the files and to distribute updates, but server infrastructure like an application server (whether it is in Java, Python, Ruby, Perl, PHP, .NET, whatever) should not be needed. Server infrastructure is needed for data APIs, but for the user interface, it should not be required.

For some larger applications, it is desirable to have the ability to create the user interface from component HTML pieces, and assemble them together in the page. Normally, an application server technology like JSP, ASP, PHP would be used to accomplish this task. However, since the desire is to avoid application server infrastructure, a View Assembly tool is needed that would provide that capability. Tagneto's View Assembly tool does just that, and it uses tools the UI developer already needs to know, JavaScript, XML, HTML and Entities.

The View Assembly tool also implements advanced features such as
  • "Aspected Oriented HTML" via overlays, allowing the matching and processing of tags across pages, and allowing processing before, after, as the first child or as the last child of the matching tag.
  • Chained tag handlers, for combining many small, focused tags together to accomplish something big.
  • The architecture is also designed to allow other scripting languages (Python, Ruby, Perl) to be used instead of JavaScript for the tag scripting (but right now only JavaScript is implemented).
  • The source does not have to completely valid XML.
  • Internationalization/Localization support.
To help with the "fast" part of "fast, rich web applications", the View Assembly tool uses a developer, compile step to generate the final UI instead of trying to do everything dynamically at runtime in JavaScript. This allows the most efficient use of processing power: use the developer's box to build things that don't depend on dynamic user input, and save only the request/user-dependent UI generation for the runtime JavaScript.
Avoiding application server infrastructure opens up the possibility to deliver the user interface from anywhere, a specialized web farm like a Content Delivery Network (CDN), or even the local disk.

However, to successfully access the data APIs, cross-domain access to the APIs via JavaScript is required. Something that is not possible with XMLHTTPRequest. Ideally, future advances of the XMLHTTPRequest object will provide that functionality, but for the browsers of today, The Dynamic Script Request API is an attempt to solve that issue.

All that said, application server infrastructure is a critical component of delivering a complete application. Hopefully it is just used for the data APIs, but the View Assembly tools should play nice with those technologies if they also need to be used to generate UI. Tagneto's View Assembly tool does play nice with the technologies, since it can be run before deploying the pages to the application server, and it is very flexible in choosing which tags to match and process.

The main goal of Tagneto is not necessarily to provide a "JavaScript library framework". Some JavaScript libraries are available with Tagneto, but in the service of the mission above. In fact, recently I have been considering providing View Assembly tool integration with some of the more well-known JavaScript frameworks, like Dojo or Prototype, with Dojo integration probably first.

Perhaps for Dojo, "View Assembly tool integration" means:
  • Using Dojo's string building utilties to build the JS-escaped strings for the ctrl tags. Right now the Array.append/join is used.
  • Supporting Dojo's connect() methods for event binding via the ctrl:listen tag.
  • Possibly do some compile-time "preprocessing" on Dojo widget tags to convert them to the HTML/JavaScript that will actually be used at runtime. This would avoid having to scan the HTML at runtime to find and bind the widgets.
  • Integrating the DSR with dojo.io.bind.
Those are just some brainstorming ideas. I'll have to investigate the Dojo code to determine feasibility. But I think one of the results of clarifying Tagneto's mission is the focus on not trying to build a JavaScript framework library. Some JS libraries will be developed as reference implementation for pieces that fulfill the mission, but ultimately, I may try to integrate those implementations into existing JS frameworks.

I'll see how it goes, but right now I like this clarification. I'll gradually work on updating the site docs to reflect it. Feedback or suggestions are welcome.

Tuesday, January 17, 2006

Using DSR to allow On-Demand JavaScript for RSS/ATOM feeds

It would be great to see data services provide a JavaScript version of their APIs to allow web developers that don't have (or want) server resources to implement data mashups. JavaScript APIs that can be used via HTML script elements allow for cross-domain data access without server intermediaries.

With the release of Tagneto 0.4.0 there is an update to the Dynamic Script Request (DSR) API, an API that makes On-Demand JavaScript via HTML script elements reliable and easy to use.

The API now uses a reserved callback function, window.onscriptload, and an event object passed to that method to communicate the success/error codes and the data associated with the response. Other browser events and the XMLHTTPRequest object were used to guide the names of the event properties.

These changes allow for data services that provide RSS/ATOM feeds (or any type of XML data feed) to provide a JavaScript response that is equivalent to the normal XML response without too much work (basically encode the XML as a JavaScript string, and put it in an event object that is passed to window.onscriptload).

The API document has more detail on how a RSS/ATOM feed might be provided as JavaScript in the Possible Applications section.

Tagneto 0.4.0 provides a Dsr.js helper script that implements the DSR API for use in a web page, and there is a DSR jar file that can be used in Java Servlet applications to send DSR responses.

Tagneto's home page uses the DSR JavaScript library and the DSR jar for the "Recent Blog Entries" section as a demonstration of the API and the implementation.

Monday, January 16, 2006

Tagneto 0.4.0 Released

The focus on 0.4.0 was on improving and simplifying the Dynamic
Script Request (DSR) API
. From the Release Notes:
  • Significant changes to the DSR API. Decided to go with a reserved global callback handler, onscriptload, to simplify things. Other browser events and the XMLHttpRequest object were used to guide the format and names of the methods and parameters. The result is a much simpler API. A sample usage of the API for RSS and ATOM feeds is described.
  • The Tagneto distribution includes a server/dsr directory. This directory contains a DSR jar that helps servers implement the DSR API for use in web applications that use Java Servlets. A sample WAR file that uses the jar is also included. The JAR and WAR file should run with JRE 1.4+.
  • The Dsr.js file has been updated to the new API.
  • The Tagneto distribution includes the JavaScript libraries in a js directory at the top of the distribution.
  • Tagneto's home page has been updated to use the new DSR js and jar libraries for the "Recent Blog Entries" section.

Sunday, January 01, 2006

Tagneto 0.3.2 Released

The main focus of the release was the JavaScript libraries that support doing dynamic script requests.

See http://tagneto.org/ReleaseNotes.html for more information.