subbu.org

Wrapping Browser Objects

without comments

Over at Ajaxian, there is a post written about reinventing XMLXttpRequest. This so-called corss-browser implementation is nothing but a wrapped XMLHttpRequest object, which is implemented in JavaScript with some of the methods wrapped partially to do some interesting things. The code is posted at http://code.google.com/p/xmlhttprequest. While at BEA, I took a similar approach for letting aggregation frameworks (such as portals) manage XMLHttpRequests between browsers and web applications (such as portlets). The approach turned out to be successful. It allowed portlets use a wrapped XMLHttpRequest directly or via Ajax frameworks such as Dojo in a loose-coupled manner. Some “security experts” also called wrapping XMLHttpRequest a security risk!

However, due to the way the XMLHttpRequest is currently drafted and how it is implemented in browsers, making such wrapped implementations conform to the XMLHttpRequest specification is impossible unless the wrapped object is implemented natively and not in JavaScript. Here is why.

Semantics of Field Access

XMLHttpRequest requires that implementations must throw certain exceptions when fields such as responseText, status are accessed at the wrong time. For example, when a client accesses the responseText field when the value of readyState is not 3 or 4, the implementation is required to raise an INVALID_STATE_ERR. For wrapped implementations implemented in ECMAScript, it is not possible to raise such an exception on field access. In ECMAScript, you can raise an exception when a method is called, but not when a field is accessed or set.

Whether this is a problem or not depends on how the wrapped XMLHttpRequest is used to process the response. Here are some possible usages:

request.onreadystatechange = function() {
if(request.readyState == 4 && request.status == 200)
...
}
}
request.onreadystatechange = function() {
if(request.status == 4 && request.readyState == 200)
...
}
}
request.onreadystatechange = function() {
if(request.responseText) {
...
}
}

With a comforming implementation of XMLHttpRequest, only the first form will run as expected. The second one will likely fail since the status may not be available until a state of readyState=3 is reached. So, if the implementation dispatches a readyState=4 to the onreadystatechange handler, accessing the status field will raise an INVALID_STATE_ERR.

The third form will fail for the same reason. Accessing the responseText field before readyState=3 will also cause an INVALID_STATE_ERR.

However, all the three forms will likely succeed in a wrapped implementation of XMLHttpRequest because the wrapped implementation can not enforce an exception when a field is accessed at the wrong time, such as when responseText is not available yet.

Until recently, only Firefox supported a way to bind JavaScript properties to getters and setters via __defineGetter__ and __defineSetter__ methods. These are also now supported in Safari 3.0. See JavaScript Getters and Setters for a good writeup with examples.

Still, it is possible to write code such that it works consistently with or without a wrapped XMLHttpRequest.

Protecting Fields

The second problem is that you can not prevent clients from modifying the field values thus breaking the semantics of the wrapped object. For instance, you can not prevent a client using a wrapped XMLHttpRequest object from modifying the responseText value. Does it matter? It depends. If your application is rendering any untrusted markup in the page, such markup can potentially modify the value of, e.g., responseText before some other part of your application has a chance to process it.

Support Not Mandatory

The third issue is that there is nothing preventing a browser from letting you override the XMLHttpRequest object. Note browsers don’t let you override other browser objects such as window, document etc. Several months ago I tried creating wrapped window and document objects, and I could not go very far.

Why Wrapped Objects?

The use cases for supporting wrapped objects are just getting stronger. Portals were one of first to encounter use cases that require some kind of managed/scoped context for rendering potentially external markup within the browser. A classic example is a portal rendering a portlet written by some external developer. In this case, portals had to resort to requiring the developer to properly namespace JavaScript, CSS etc. In some cases, portlet developers were told not to rely on objects such as document and window as those objects refer to the overall page and not just the portlet. In the case of Ajax, portlet developers were told not to use the XMLHttpRequest object in arbitrary ways. All these cases could be simplified by properly wrapping the underlying browser objects providing a narrower scope to markup fragments.

The folks at Facebook are grappling with some of the same issues. See FBJS.

  • Digg
  • del.icio.us
  • Google

October 26th, 2007 at 3:45 pm

RSS feed | Trackback URI

Comments »

No comments yet.

Name (required)
E-mail (required - never shown publicly)
URI
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.

Trackback responses to this post