Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

XDomainRequest’s hidden requirements

Jerry Stratton, July 23, 2012

Microsoft Internet Explorer 8 and 9 do not support cross-domain requests and Access-Control-Allow-Origin with XMLHttpRequest. If you’re doing an XML request to get information from a service that isn’t on the page’s server1, IE 8 and 9 don’t support it. Microsoft has a different method for grabbing cross-domain requests, XDomainRequest.

The biggest problem I ran into is that most of the examples I saw of XDomainRequest mirrored the use of XMLHttpRequest’s synchronous mode. I suspect they took the simple version from Microsoft’s XDomainRequest page without following through to the working version:

  • // 1. Create XDR object
  • var xdr = new XDomainRequest();
  • // 2. Open connection with server using GET method
  • xdr.open("get", "http://www.contoso.com/xdr.aspx");
  • // 3. Send string data to server
  • xdr.send();

For all practical applications that require a response, that doesn’t work. XMLHttpRequest accepts a “false” as the third parameter to turn off asynchronous operation. XDomainRequest does not. XDomainRequest only works asynchronously. That means you have to create a callback method to execute once the response is fully loaded; the callback method is onload. Otherwise, sometimes XDomainRequest will appear to work and sometimes it will appear not to work, depending on whether the asynchronous request has completed by the time you access the responseText property.

It’s probably not a bad idea to use XMLHttpRequest asynchronously as well, although it’s often a bit of overkill with requests for a couple of bytes to a nearby server.

[toggle code]

  • <script type="text/javascript">
    • function verifyAvailability() {
      • //first, attempt to get the XML response using standard JavaScript
      • try {
        • var xmlhttp = new XMLHttpRequest();
        • xmlhttp.open("GET", "https://appserver.example.com/service/availability/");
        • xmlhttp.onload = function() {
          • actOnResponse(xmlhttp.responseXML);
        • }
        • xmlhttp.send();
      • } catch(error) {
        • var http = new XDomainRequest();
        • http.open("get", "http://proxy.example.com/service/availability/");
        • http.onload = function() {
          • //IE 8 does not enable the DOMParser by default? Awesome, if true.
          • var response;
          • if (window.DOMParser) {
            • //this is untested
            • var parser = new window.DOMParser();
            • response = parser.parseFromString(http.responseText, "text/xml")
          • } else {
            • response = new ActiveXObject("Microsoft.XMLDOM");
            • response.async=false;
            • response.loadXML(http.responseText);
          • }
          • actOnResponse(response);
        • }
        • http.send();
      • }
    • }
    • function actOnResponse(response) {
      • var availability = response.getElementsByTagName("availability")[0].childNodes[0].nodeValue;
      • if (availability == 'on') {
        • window.alert("Turning it on");
      • }
    • }
    • window.onload=verifyAvailability;
  • </script>

The biggest part of the Internet Explorer XDomainRequest fallback is parsing the XML. XMLHttpRequest will parse the response as XML automatically2 and populate responseXML with the resulting XML document. XDomainRequest does not, so you’ll have to do it yourself. The most obvious choice would be the DOMParser, but, at least in IE 8 on the computer I had to use for testing while building the function, DOMParser functionality was not turned on by default; it appears to be up to the user to turn it on, and that’s obviously not something we can rely on. So if the DOMParser isn’t available I use the ActiveXObject equivalent.

That, however, isn’t everything: if you look closely, you’ll see a different request URL in the IE error handling section than in the main version. That’s because our application server always serves over SSL; the particular page that needed to make the cross-domain request is not served over SSL. For XDomainRequest, however, it isn’t just that you can’t make insecure requests from secure pages: the protocols must always match; you can’t make an https request from an http page either.3

So what started as a very simple XML request to a service to see if it’s available goes from about three lines of code in WebKit/Mozilla, to three lines of code plus eleven lines of error handling to cover IE.

To summarize, there are three issues that I ran into when using XDomainRequest to replace XMLHttpRequest in Internet Explorer 8 and 9:

  1. XDomainRequest is always asynchronous, which means you have to use a handler to handle the response.
  2. XDomainRequest does not parse XML; you’ll have to parse it yourself.
  3. XDomainRequest cannot load a secure response from an insecure page.
  1. I can’t find any information on whether cross-domain is marked using the page as reference or the script.

  2. At least as long as the response is text/xml or (most likely) application/xml.

  3. “However, this restriction is overly broad, because it prevents HTTP pages from issuing XDomainRequests targeted to HTTPS pages. While it’s true that the HTTP page itself may have been compromised, there’s no reason that it should be forbidden from receiving public resources securely.”

  1. <- ModelForms and FormViews
  2. ajax_selects custom forms ->