views:

357

answers:

2

I developed a web app using GWT about 2 years ago, since then the application has evolved. In its current state it relies on fetching a single XML file and parsing the information from it. Overall this works great. A requirement of this app is that it needs to be able to be ran from the filesystem (file:///..) as well as the traditional model of running from a webserver (http://...)

Fetching this file from a webserver works exactly as expected using a RequestBuilder object. When running the app from the filesystem Firefox, Opera, Safari, and Chrome all behave as expected. When running the app from the filesystem using IE7 or IE8 the RequestBuilder.send() call fails, the information about the error suggests that there is a problem accessing the file due to violating the same origin policy. The app worked as expected in IE6 but not in IE7 or IE8.

So I looked at the source code of RequestBuilder.java and saw that the actual request was being executed with an XMLHttpRequest GWT object. So I looked at the source code for XMLHttpRequest.java and found out some information.

Here is the code (starts at line 83 in XMLHttpRequest.java)

  public static native XMLHttpRequest create() /*-{
    if ($wnd.XMLHttpRequest) {
      return new XMLHttpRequest();
    } else {
      try {
        return new ActiveXObject('MSXML2.XMLHTTP.3.0');
      } catch (e) {
        return new ActiveXObject("Microsoft.XMLHTTP");
      }
    }
  }-*/;

So basically if an XMLHttpRequest cannot be created (like in IE6 because it is not available) an ActiveXObject is used instead.

I read up a little bit more on the IE implementation of XMLHttpRequest, and it appears that it is only supported for interacting with files on a webserver.

I found a setting in IE8 (Tools->Internet Options->Advanced->Security->Enable native XMLHTTP support), when I uncheck this box my app works. I assume this is because I am more of less telling IE to not use their implementation of XmlHttpRequest, so GWT just uses an ActiveXObject because it doesn't think the native XmlHttpRequest is available.

This fixes the problem, but is hardly a long term solution.

I can currently catch a failed send request and verify that it was trying to fetch the XML file from the filesystem using normal GWT. What I would like to do in this case is catch the IE7 and IE8 case and have them use a ActiveXObject instead of a native XmlHttpRequest object.

There was a posting on the GWT google group that had a supposed solution for this problem (link). Looking at it I can tell that it was created for an older version of GWT. I am using the latest release and think that this is more or less what I would like to do (use GWT deferred binding to detect a specific browser type and run my own implementation of XMLHttpRequest.java in place of the built in GWT implementation).

Here is the code that I am trying to use

package com.mycompany.myapp.client;

import com.google.gwt.xhr.client.XMLHttpRequest;

public class XMLHttpRequestIE7or8 extends XMLHttpRequest
{
    // commented out the "override" so that eclipse and the ant build script don't throw errors
    //@Override
    public static native XMLHttpRequest create()
    /*-{
        try
       {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
       }
       catch (e)
       {
           return new ActiveXObject("Microsoft.XMLHTTP");
       }
    }-*/;

    // have an empty protected constructor so the ant build script doesn't throw errors
    // the actual XMLHttpRequest constructor is empty as well so this shouldn't cause any problems
    protected XMLHttpRequestIE7or8()
    {
    }
};

And here are the lines that I added to my module xml

<replace-with class="com.mycompany.myapp.client.XMLHttpRequestIE7or8">
    <when-type-is class="com.google.gwt.xhr.client.XMLHttpRequest"/>
    <any>
        <when-property-is name="user.agent" value="ie6" />
        <when-property-is name="user.agent" value="ie8" />
    </any>
</replace-with>

From what I can tell this should work, but my code never runs.

Does anyone have any idea of what I am doing wrong?

Should I not do this via deferred binding and just use native javascript when I catch the fail case instead?

Is there a different way of approaching this problem that I have not mentioned?

All replies are welcome.

+1  A: 

Note that ie7 is not a valid value for user.agent - ie6 is the value for both Internet Explorer 6 and Internet Explorer 7 (see UserAgent.gwt.xml for how user.agent is computed).

Perhaps this invalid value is causing GWT to avoid using your code altogether?

aem
thank you for the suggestion. I have updated my code and question with this information but unfortunately the problem still exists
snctln
A: 

You are missing one key thing - a call to GWT.create

For deferred binding to work, somebody has to call GWT.create on the class com.google.gwt.xhr.client.XMLHttpRequest - only then will you get an implementation specific to your needs. If you look at RequestBuilder.java, it directly instantiates a XMLHttpRequest object on the first line of deSend() method. That is, there is no call to GWT.create(), and hence your IE-specific implementation does not get picked up.

To solve the problem, you have to replace this line

XMLHttpRequest xmlHttpRequest = XMLHttpRequest.create();

with

XMLHttpRequest xmlHttpRequest = GWT.create(XMLHttpRequest.class);

So, you either modify the source of GWT and re-compile (YUCK!), or you do something like this -

  1. Sub-class RequestBuilder and override sendRequest() method.
  2. Copy/Paste everything from doSend() method BUT replace the one line I mentioned above
  3. Do a blind-grep in your code, and replace all instances of RequestBuilder with MyRequestBuilder

I guess that should solve your problem.

sri