Thursday, November 30, 2006

Cache in with JSON

Data validation is one of the most challenging and ever-changing parts of any enterprise Web application. Quite often validation metadata leaves JavaScript modules littered with server-side code. In this article, you'll learn an elegant way to cache metadata on the client side with the help of server code, which provides JSON-formatted (JavaScript Object Notation) stringified metadata. This approach also enables you to handle multivalue and multigroup attributes in a manner similar to Ajax.

Each application targets some domain problem. And each domain has its own set of rules and regulations that put constraints on data. When an application applies those constraints on data, the constraints become validations. All applications need to validate the data that users enter.

Today, applications generally use combinations of if-else statements to validate data. These statements contain validation data that developers either hard-code or put through server-side code. Generally, developers use server-side code to avoid small data changes that can lead to changes in JavaServer Pages (JSP).

You can use JavaScript Object Notation (JSON) to group and cache metadata and use the JavaScript function to access that metadata to validate the user input.

When you have metadata scattered over JavaScript, you can't control how much data the server evaluates and goes to the client. Instead, all server-side code pieces are evaluated and sent to the server. However, when you cache data using JSON, you have full control over how much metadata you send to the client because server-side code generates JSON-formatted metadata. This helps send only metadata to the client that corresponds to the user who will see or enter the data.

You can also use JSON to cache data that the user inputs. Once the program caches the data, it erases the data fields rather than refreshing the screen, similar to Ajax. This way a user can enter another set of data for the same property.

Let's explore metadata caching using JSON.

JSON in brief

With JSON, or JavaScript Object Notation, you represent your JavaScript object in a specific string format. If you assign a string with such a format to any JavaScript variable, the variable will then refer to an object that is constructed from a string assigned to it.

For example, suppose you have a policy object that has these attributes:

* Plan Name
* Description
* Duration

You can represent this policy object in JSON format using the following string:

{"Plan":"Full Life Cover", "Description":"The best life insurance plan", "Term":"20 years"}

If you assign this string to any JavaScript variable, the variable will accept the data in terms of an object. To access the data, give the path of the attribute you want to access. For this example, assign the above string to a variable called policy:

var policy = {"Plan":"Full Life Cover", "Description":"The best life insurance plan", "Term":"20 years"}

Paste this string in your HTML page's header section and write the following alert:

alert(policy.Plan)

If you see this page in any JavaScript-supported browser, you will see that the alert displays the policy plan.

The example

To demonstrate JSON's capabilities, you will take a person object that has a list of vehicle objects and take an object person that can hold one or more vehicles. Each vehicle has the following properties:

* Brand
* Registration Number
* CC

The browser UI should allow users to add multiple vehicles with best application performance (usually an inherent requirement). Each property has some restriction or validation rule attached to it. You'll assign the following rules:

* Brand Name
o Brand Name can never contain a digit.
o Brand Name can contain a maximum of two words separated by a space.
* Registration Number
o Registration Number must be all digits.
* CC
o CC must be all digits.
o CC can be a minimum of 50 and maximum 5000.

You will have three input fields corresponding to the vehicle properties, where a user will enter the information. Next, you'll see how to group the validation messages in a JSON group and how to access them.

Conventional approach

Now, when a user enters 40 CC for the vehicle data, the program must display a message saying that the entry does not fall within the valid CC range. You can show the message simply as in Listing 1:

Listing 1. Conventional code

if(cc < <%= minCC %> || cc > <%= maxCC %>) {
alert(<%= ResourceList.vehicleCCRangeMsg >);
}


ResourceList is a server-side class that holds the internationalized messages in variables like vehicleCCRangeMsg. This approach solves the problems with little mess:

1. This way you add server-side code to all client-side validation functions in order to check conditions and to show messages.
2. If you change the way you organize metadata and messages (such as the server-side classes or variables), you end up changing client script validation functions that use them.

How JSON can help

How would you feel if you have to refer only to a JavaScript variable inside condition statements and alerts rather than server-side code? You won't have server-side code in your JavaScript and the change in server-side metadata and message holding will not affect the client-side script. This would be great, right? Well, that is exactly what you will do when you employ JSON-based metadata caching.

You will use a JavaScript object to group our validation data and messages in a hierarchy. And you will access these messages just like you access the hierarchical JavaScript object. That's it and you are done!

Once you have this JSON metadata object in place, your previous piece of JavaScript will look like Listing 2.

Listing 2. Alert with JSON metadata caching object

if(cc <> vehicleValidationsMetadata.CC.maxCC) {
alert(vehicleValidationsMetadata.CC.RangeMessage);
}


The question now is who or what will prepare the JSON metadata object? Well, only the server can do that. The server must produce this JSON object and provide it to the client (browser). Some Java APIs help you prepare such (in fact, any kind of) JSON objects. See Resources to find those APIs.

A typical approach to generate a JSON metadata object is

1. You prepare a hierarchical Java object for your entities and their validation messages
2. Call toString() on them. These would mostly give you a JSON-formatted string.
3. Store that string away in a request scope.
4. In JSP, get that string and assign it inside the curly brackets of a JavaScript variable value.

The final vehicle metadata object might look like Listing 3.

Listing 3. Validation metadata JSON object

var vehicleValidationsMetadata = {
"BrandName":{
"CanContainDigits":false,
"MaxWords":2,
"FormatMessage":"Brand Name cannot contain digits."
"WordLimitMessage":"Brand Name cannot contain more than two words"
},
"RegistrationNumber":{
"CanContainAlphabets"false,
"CanContainDigits":true,
"FormatMessage":"Registration Number can contain only digits."
},
"CC"
"minCC":50,
"maxCC"5000,
"FormatMessage": "can only be numeric",
"RangeMessage": "CC can be within range of 50 and 5000"
}
}


The server must produce the entire string, except for the first and last lines, because the current user locale might require these messages (and only server-side code can accomplish this). One thing to note here is that this metadata object is only for validating the vehicle. The better encapsulation is if the vehicle metadata object is part of the person metadata object. In that case, rather than create another JavaScript variable, you can just include that metadata object into your person metadata object.

Once you have this metadata object ready, you can use the metadata and messages in that object to validate data input and display messages. Now your JavaScript function that validates vehicle inputs might look like Listing 4.

Listing 4. Vehicle data validation function

function validateVehicleData() {
var brandName = //get brand name from form field
var registrationNumber = //get Registration Number from form field.
var CC = //get CC from form field
var brandNameTokens = brandName.split(' ');
if(brandNameTokens.length > vehicleValidationsMetadata.BrandName.MaxWords) {
alert(vehicleValidationMessages.BrandName.WordLimitMessage);
}
.
.
.
if((!vehicleValidationsMetadata.RegistrationNumber.CanContainAlphabets) &&
isNaN(parseInt(registrationNumber))) {
alert(vehicleValidationMessages.RegistrationNumber.FormatMessage);
}
var ccNum = parseInt(CC);
if(ccNum <> vehicleValidationMessages.CC.maxCC) {
alert(vehicleValidationMessages.CC.RangeMessage);
}
}


Doesn't this code look better? It doesn't have server code littered in JavaScript. It does not need to re-write client scripts if the server side changes the way it stores metadata. It makes the life of a JSP programmer much easier.

Extending client-side data caching

Some Web applications require users to enter multiple data for the same property or object. As an example, the person-vehicle requires a person to enter data for each vehicle she owns. If she owns more than one vehicle, the application must allow her to enter data of more than one vehicle. I will refer to this kind of object as a multigroup attribute. If the multigroup attribute contains any property that can hold multiple data instances, I will call that multivalue attribute.

Now, the problem with multigroup and multivalue attributes is that you have to enter the data in the same input fields. That means before you enter data for the second vehicle, you have to first save the data you entered for the first vehicle. You can solve this problem two ways:

1. Send the first vehicle's data to the server and blank out the input fields to allow the user to enter the next vehicle's data.
2. Cache the data on the client and blank out the input fields to allow the user to enter the next vehicle's data.

The problem with the first approach is that it needs a server visit for each vehicle data entered. This isn't pretty; users will become frustrated when they have to wait for a server response after they enter vehicle data. Alternatively, the second approach has almost zero response time. The user can enter all vehicle data quickly without waiting. But the matter of concern here is how you cache the data on the client side. Here are more ways to store the data on the client:

1. Cache the data in some format into hidden form field(s) as the user clicks to add the next vehicle's data.
2. Cache data into a JavaScript object.

When you store data into hidden fields, you end up manipulating many hidden fields or manipulating hidden field data every time the user enters new vehicle data. This is like you frequently manipulating a string with string operations.

But the second form of caching data offers an object-oriented approach to caching. When the user enters new vehicle data, you create a new element in the array object. There are no clumsy string operations. When the user is done with all the vehicles, you can simply form a JSON string out of that object and send it to the server by storing it in some hidden field. This approach is much more elegant than the first one.

JSON, data caching and Ajax abilities

When you cache data on the client side using JSON, you update the data caching object every time the user clicks on the Add Vehicle button. The JavaScript function to accomplish this task might look like Listing 5.

Listing 5. Function to add vehicle data into JavaScript object for client-side caching

function addVehicleData() {
var brand = //get vehicle brand;
var regNo = //get registration number;
var cc = //get cc;

vehicleData[vehicleData.length] = new Object();
vehicleData[vehicleData.length].brandName = new Object();
vehicleData[vehicleData.length].brandName = brand;
//same way update other two properties
}


Here vehicleData is the JavaScript variable that initializes when a user loads the page. It is initialized to a new array object, which is empty or has vehicle elements from when a user entered vehicles earlier.

Once this function saves the data into a JavaScript object, the program can invoke another function that will clear out the input fields to allow a user to enter new data.

In such applications, you will require the user to enter certain minimum or maximum number of occurrences of multigroup or multivalue attributes. You can put these limits into a JSON metadata object. In this case, your earlier metadata object will look like Listing 6.

Listing 6. JSON metadata object with occurrence limits

var vehicleValidationsMetadata = {
"MIN_OCC":0,
"MAX_OCC":10,
"MAX_OCC_MSG":" Your message....",
"MIN_OCC_MSG":" Your message.....",
//Everything else is the same
}


Then your addVehicleData() function will validate the data on occurrences first and will add data to the JavaScript object only if the total occurrences are within the allowed limits. Listing 7 shows how you check this.

Listing 7. JSON metadata object limit check


function addVehicleData() {
if(vehicleData.length == vehicleValidationsMetadata.MAX_OCC-1) {
alert(vehicleValidationsMetadata.MAX_OCC_MSG);
}
//Everything else is the same
}


The function that is called when a user submits a page actually validates for minimum occurrences. The biggest advantage of this approach is that the screen doesn't refresh to enter new vehicle data. Providing such static screens was the primary objective of Ajax technology and you can accomplish this with JSON as well. This is all about updating the JSON data object and manipulating the HTML DOM tree through JavaScript. The user response time is minimal as everything executes on the client side only. You can use JSON to provide Ajax abilities to your application.

When a user clicks the Save button, the program calls another JavaScript function that will stringify this JSON object and store it in the hidden form field that the program submits to the server. JSON.js (see Resources) has a JSON.stringify() function that takes the JavaScript object as input and returns string output.

The server side has to be able to understand a JSON-formatted string and produce a server-side object in order to proceed and save the data. The Web site http://www.json.org/java/index.html offers a Java API that serves most of these needs for Java-based applications.

Conclusion

You saw powerful uses for JSON in this article. To summarize:

1. JSON provides an elegant and object-oriented way to cache metadata on client.
2. JSON helps separate validation data and logic.
3. JSON helps provide an Ajaxian nature to a Web application.

Cross Site Scripting Vulnerability in Google

Google is vulnerable to cross site scripting. While surfing around the personalization section of Google I ran accross the RSS feed addition tool which is vulnerable to XSS. The employees at Google were aware of XSS as they protected against it as an error condition, however if you input a valid URL (like my RSS feed) it will return with a JavaScript function containing the URL.

If you append the URL of the valid feed with a query string that contains your cross site scripting exploit Google will not sanitize it upon output of the JavaScript (it will upon screen render of the main page, but at that point it is too late). The JavaScript is not intended to be rendered directly, but that’s irrelevant, and can be exploited directly. Because this lives on the http://www.google.com/ domain it is not subject to cross domain policy restrictions that have typically protected Google from these attacks in the past.

Here is a screenshot of the vulnerability:

Cross Site Scripting Vulnerability in Google
(click to enlarge)

If you want to see the vulnerability for yourself click here (this is a benign proof of concept). As I said, this is using the query string from a valid feed to inject the vector. It doesn’t work if you inject it into the Add Content function on the page because the page itself sanitizes the output. Unfortunately for Google this can be intercepted far earlier than the page that does the eventual sanitization. One of the worst parts of this is that it does not require you to be logged in to exploit this cross site scripting vulnerability.

Additionally. in a few seconds of searching, I also found that Google has yet another URL redirection attack in it that can be used for phishing attacks located here (will redirect you to a benign site that demonstrates the attack). Google has been pretty notoriously slow at fixing these sorts of attacks in a timely manner (the last one that was actually being used by phishers was open for nearly a month), but they are really bad, because phishers can easily bounce their traffic off of these trusted domains. People are far more likely to click on a website that says www.google.com than they are to click on a site that says www.wellfsarg0.com or something equally obvious. I understand they are used for tracking purposes, but there are ways around this, like checking against whitelists, or checking against an embedded hash, etc. It’s processor intensive, but it protects the internet community.

Also in a few minutes of checking, id found a CSRF in Google (cross site request forgery) where malicious websites can change the default map search location. This is really not a big deal as far as I can tell besides annoying Google and it’s users, but it’s worth mentioning. Make sure you are logged into Google and then click on the following CSRF to change your default location to the whitehouse. Annoying, but I doubt there is a bigger hole here. The point is that Google definitely has not protected against CSRF, and I am sure there are additional vulnerabilities here that I have not played with since I only spent a few minutes looking at it.

So back to the cross site scripting vector, since that is by far the most dangerous. What are the implications of this attack for Google? Well, for starters, I can put a phishing site on Google. “Sign up for Google World Beta.” I can steal cookies to log in as the user in question, I can use the credentials of the user to screen scrape any of the content off of the www cname, including changing options like adding my RSS feed to your page, or deleting them, etc… I can steal your phone number from the /sendtophone application via an XML RPC (AJAX) call via a POST method, get your address because maps.google.com is mirrored on http://www.google.com/maphp?hl=en&tab=wl&q= etc… the list of potential vulnerabilities goes on and on. The vulnerabilities only grow as Google builds out their portal experience.

Indeed this also could have massive blackhat SEO (Search Engine Optimization) implications as Google sets itself as the authority in page rank (above other sites with more traffic). Its own page is set as a 10 in page rank. Trusting yourself could actually prove to be dangerous in this case, although this is a theoretical attack. Injecting your own links and getting engines to spider it could temporarily dramatically inflate page rank as /ig/ (where the vulnerable function is stored) is not disallowed by Google’s robots.txt file (again this is a theoretical attack and it is easy for Google to de-list violators).

Ultimately, Google cannot be trusted implicitly because of these types of holes, in the same way any major site cannot be trusted implicitly for the same reason. There are too many potential issues in their applications, and your information is definitely not 100% safe when entered there.

This will become particularly relevant at Jeremiah Grossman’s talk at Blackhat next month, where he starts to go into the real issues with cross site scripting, and how dangerous these forms of attack really can be (far beyond what is currently well known). Can you tell I’m excited? I don’t particularly blame Google, as all major websites are vulnerable to this, in my experience, it’s just that with a site’s popularity it becomes exponentially more dangerous and the responsibility to find these issues before the security community increases at the same rate.

Ten Worst Internet Acquisitions Ever

As the market for acquiring fledgling Internet companies heats up, it's worth taking a look at all those acquisitions that didn't quite work out. For every Internet acquisition that's successful there seems to be dozens that die on the vine.

So what makes for a really bad Internet acquisition? First, it has to be expensive. No one's going to rake a company over the coals over a few blown $50 million acquisitions. That might sound like a lot of money to you and me, but that's a rounding error to Google.

Second, for an acquisition to be lousy it has to contribute little or no long term growth to the acquiring company. An acquisition that doesn't fit with a company's long term strategy and that is quickly forgotten - that's a bad buy.

So, here is my highly subjective list of the 10 worst Internet acquisitions of all time:

12. Myspace - acquired by News Corporation, making one of its largest bets on the Internet, announced that it is paying $580 million in cash to acquire Intermix Media Inc., a Los Angeles-based company whose chief asset is MySpace.com, a Web site that is enjoying surging popularity with young audiences.

11. Hotmail - acquired by Microsoft (MSFT) in 1998 for about $400 million. Hotmail was a second-tier free email service when Microsoft bought it and the acquisition did little to improve Microsoft's internet portal ambitions.

10. Skype - acquired by eBay (EBAY) in September 2005 for $2.6 billion. While it's early to call this one an absolute dud, eBay does not seem to have a plan - or at least a plan that would justify the acquisition price - for how to integrate Skype's calling service with the core auction business.

9. MySimon - acquired by CNET (CNET) in 1999 for $700 million. The price comparison site mySimon was supposed to launch CNET into lots of non-tech verticals - not a bad idea at the time. Unfortunately CNET had no idea how to effectively integrate mySimon and it's now withering away, surpassed by newer, shinier price comparison engines.

8 BlueMountain.com - acquired by Excite@Home in 1999. $780 million for an online greeting card site. 'Nuff said.

7. Youtube - acquired by google for $1.65 billion in 2006 million for google videos broadcasting .

6. Lycos - acquired by Terra Networks for $4.6 billion in 2000. Yeah, I never heard of Terra either. The warning bells should have gone off when the deal was originally announced in May 2000 at a value of $12.5 billion, only to fall by more than 50% by the time it closed in October of that year because each company's stock price was plummeting.

5. Netscape - acquired by AOL (TWX) in 1998 for $4.2 billion. To be fair, this was a mercy acquisition. By the time AOL bought the company, Netscape had been humbled by Microsoft's free Internet Explorer browser. AOL clearly had no plans for Netscape and as a result the once pioneering company is now an afterthought.

4. GeoCities - acquired by Yahoo! (YHOO) in 1999 for $3.56 billion. When was the last time you visited a site with a geocities.com domain? I can't remember either. Shortly after the acquisition, innovation on GeoCities appears to have ground to a halt. GeoCities could have been MySpace, but the entire social networking revolution passed them right by.

3. Excite
- acquired by @Home in 1999 for $6.7 billion. Remember Excite.com? Remember how it was the #2 or 3 portal for awhile? Well, a whole year and a half after the cable company @Home acquired Excite (for $394 per user!) in January 1999, the combined entity filed for bankruptcy never to be heard from again. Classically disastrous.

2. AOL - merged with TimeWarner in 2000. This one is obvious. While Time Warner finally seems to be turning things around at AOL six years after the fact, this merger was doomed from the start. Shortly after the merger AOL's business started falling apart fast, with TimeWarner holding the bag. There was never a coherent integration plan and all that talk of synergy is - thankfully - dead and gone.

1. Broadcast.com - acquired by Yahoo! in 1999 for $5 billion. Yahoo! paid a mind-boggling $710 per user back in the hey day of the bubble. But why does this rank higher than the AOL boondoggle? Two words: Mark Cuban. Yahoo's ludicrous overpayment for Broadcast.com gave Cuban the money to go out and buy the Dallas Mavericks basketball team and permanently implant himself on the American psyche. Unforgivable.

Dynamic HTML and XML: XMLHttprequest Object

As deployment of XML data and web services becomes more widespread, you may occasionally find it convenient to connect an HTML presentation directly to XML data for interim updates without reloading the page. Thanks to the little-known XMLHttpRequest object, an increasing range of web clients can retrieve and submit XML data directly, all in the background. To convert retrieved XML data into renderable HTML content, rely on the client-side Document Object Model (DOM) to read the XML document node tree and compose HTML elements that the user sees.

History and Support

Microsoft first implemented the XMLHttpRequest object in Internet Explorer 5 for Windows as an ActiveX object. Engineers on the Mozilla project implemented a compatible native version for Mozilla 1.0 (and Netscape 7). Apple has done the same starting with Safari 1.2.

Similar functionality is covered in a proposed W3C standard, Document Object Model (DOM) Level 3 Load and Save Specification. In the meantime, growing support for the XMLHttpRequest object means that is has become a de facto standard that will likely be supported even after the W3C specification becomes final and starts being implemented in released browsers (whenever that might be).

Creating the Object

Creating an instance of the XMLHttpRequest object requires branching syntax to account for browser differences in the way instances of the object are generated. For Safari and Mozilla, a simple call to the object's constructor function does the job:

var req = new XMLHttpRequest();

For the ActiveX branch, pass the name of the object to the ActiveX constructor:

var req = new ActiveXObject("Microsoft.XMLHTTP");

The object reference returned by both constructors is to an abstract object that works entirely out of view of the user. Its methods control all operations, while its properties hold, among other things, various data pieces returned from the server.

Object Methods

Instances of the XMLHttpRequest object in all supported environments share a concise, but powerful, list of methods and properties. Table 1 shows the methods supported by Safari 1.2, Mozilla, and Windows IE 5 or later.

Common XMLHttpRequest Object Methods

MethodDescription
abort()Stops the current request
getAllResponseHeaders()Returns complete set of headers (labels and values) as a string
getResponseHeader("headerLabel")Returns the string value of a single header label
open("method", "URL"[, asyncFlag[, "userName"[, "password"]]])Assigns destination URL, method, and other optional attributes of a pending request
send(content)Transmits the request, optionally with postable string or DOM object data
setRequestHeader("label", "value")Assigns a label/value pair to the header to be sent with a request


Of the methods shown in Table 1, the open() and send() methods are the ones you'll likely use most. The first, open(), sets the scene for an upcoming operation. Two required parameters are the HTTP method you intend for the request and the URL for the connection. For the method parameter, use "GET" on operations that are primarily data retrieval requests; use "POST" on operations that send data to the server, especially if the length of the outgoing data is potentially greater than 512 bytes. The URL may be either a complete or relative URL (but see security issues below).

An important optional third parameter is a Boolean value that controls whether the upcoming transaction should be handled asynchronously. The default behavior (true) is to act asynchronously, which means that script processing carries on immediately after the send() method is invoked, without waiting for a response. If you set this value to false, however, the script waits for the request to be sent and for a response to arrive from the server. While it might seem like a good idea to wait for a response before continuing processing, you run the risk of having your script hang if a network or server problem prevents completion of the transaction. It is safer to send asynchronously and design your code around the onreadystatechange event for the request object.

The following generic function includes branched object creation, event handler assignment, and submission of a GET request. A single function argument is a string containing the desired URL. The function assumes that a global variable, req, receives the value returned from the object constructors. Using a global variable here allows the response values to be accessed freely inside other functions elsewhere on the page. Also assumed in this example is the existence of a processReqChange() function that will handle changes to the state of the request object.

var req;

function loadXMLDoc(url) {
req = false;
// branch for native XMLHttpRequest object
if(window.XMLHttpRequest && !(window.ActiveXObject)) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
// branch for IE/Windows ActiveX version
} else if(window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
if(req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send("");
}
}

Note: It is essential that the data returned from the server be sent with a Content-Type set to text/xml. Content that is sent as text/plain or text/html is accepted by the instance of the request object however it will only be available for use via the responseText property.

Object Properties

Once a request has been sent, scripts can look to several properties that all implementations have in common, shown in Table 2. All properties are read-only.

Common XMLHttpRequest Object Properties

PropertyDescription
onreadystatechangeEvent handler for an event that fires at every state change
readyStateObject status integer:
0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete
responseTextString version of data returned from server process
responseXMLDOM-compatible document object of data returned from server process
statusNumeric code returned by server, such as 404 for "Not Found" or 200 for "OK"
statusTextString message accompanying the status code


Use the readyState property inside the event handler function that processes request object state change events. While the object may undergo interim state changes during its creation and processing, the value that signals the completion of the transaction is 4.

You still need more confirmation that the transaction completed successfully before daring to operate on the results. Read the status or statusText properties to determine the success or failure of the operation. Respective property values of 200 and OK indicate success.

Access data returned from the server via the responseText or responseXML properties. The former provides only a string representation of the data. More powerful, however, is the XML document object in the responseXML property. This object is a full-fledged document node object (a DOM nodeType of 9), which can be examined and parsed using W3C Document Object Model (DOM) node tree methods and properties. Note, however, that this is an XML, rather than HTML, document, meaning that you cannot count on the DOM's HTML module methods and properties. This is not really a restriction because the Core DOM module gives you ample ways of finding element nodes, element attribute values, and text nodes nested inside elements.

The following listing shows a skeletal onreadystatechange event handler function that allows processing of the response content only if all conditions are right.

function processReqChange() {
// only if req shows "loaded"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
// ...processing statements go here...
} else {
alert("There was a problem retrieving the XML data:\n" +
req.statusText);
}
}
}

If you are concerned about possible timeouts of your server process, you can modify the loadXMLDoc() function to save a global time-stamp of the send() method, and then modify the event handler function to calculate the elapsed time with each firing of the event. If the time exceeds an acceptable limit, then invoke the req.abort() method to cancel the send operation, and alert the user about the failure.

Security Issues

When the XMLHttpRequest object operates within a browser, it adopts the same-domain security policies of typical JavaScript activity (sharing the same "sandbox," as it were). This has some important implications that will impact your application of this feature.

First, on most browsers supporting this functionality, the page that bears scripts accessing the object needs to be retrieved via http: protocol, meaning that you won't be able to test the pages from a local hard disk (file: protocol) without some extra security issues cropping up, especially in Mozilla and IE on Windows. In fact, Mozilla requires that you wrap access to the object inside UniversalBrowserRead security privileges. IE, on the other hand, simply displays an alert to the user that a potentially unsafe activity may be going on and offers a chance to cancel.

Second, the domain of the URL request destination must be the same as the one that serves up the page containing the script. This means, unfortunately, that client-side scripts cannot fetch web service data from other sources, and blend that data into a page. Everything must come from the same domain. Under these circumstances, you don't have to worry about security alerts frightening your users.

An Example: Reading XML Data from iTunes RSS Feeds

You can play with an example that points to four static XML files for demonstration purposes. The data sources are snapshots of some iTunes Store-related RSS feeds. Because the actual feeds are hosted at a third-party domain, the mixed domains of the example file and live RSS sources prevents a truly dynamic example.

When you choose one of the four listing categories, the script loads the associated XML file for that category. Further scripts extract various element data from the XML file to modify the options in a second select element. A click on one of the items reads a different element within that item's XML data. That data happens to be HTML content, which is displayed within the example page without reloading the page.

Note that the sample data includes some elements whose tag names contain namespace designations. Internet Explorer for Windows (at least through Version 6) does not implement the DOM getElementsByTagNameNS() function. Instead it treats namespace tag names literally. For example, the element is treated as an element whose tag name is content:encoded, rather than a tag whose local name is encoded and whose prefix is content. The example includes a utility API function called getElementTextNS() which handles the object model disparities, while also retrieving the text node contained by the desired element.

If you download the examples (DMG 2.0MB), you can test it yourself by placing it in your personal web server's Sites folder, and accessing the page via the http: protocol. Keep all files, including the XML samples, in the same directory.

A Cool Combo

For years, advanced client-side developers have frequently wanted a clean way to maintain a "connection" with the server so that transactions can occur in the background, and newly updated data gets inserted into the current page. Many have tortured themselves by using techniques such as hidden self-refreshing frames and "faceless" Java applets. In lieu of a W3C standard still under development, the Microsoft-born XMLHttpRequest object fills an important gap that should inspire application development creativity. The feature is a welcome addition to Safari.