views:

464

answers:

4

Everything works perfect in Firefox and Chrome but except in IE8 (8.0.6001.18702)

This is the test code (Jquery 1.4.2) (same problem with $.post):

$(function() {
   $.get("http://domain2.tld/some.php", {}, function(response) {
       alert(response);
   });
});

This code is executed in domain1.tld and it is loaded from domain2.tld, this way:

<script type="text/javascript" src="http://domain2.tld/test.js"&gt;&lt;/script&gt;

I'm getting a "Permission denied" message in IE8. I have tried so far without success:

1) adding in domain1.tld (php code):

header("X-XSS-Protection: 0");

2) disabling XSS filter in IE8 options.

I'm using IE8 debugger and it shows the error in line 5113:

xhr.open(type, s.url, s.async);

If instead of calling $.get(domain2.tld ...), I call $.get(domain1.tld ...) there is no error, which confirms to me that is an XSS "same origin policy" problem.

My only solution (I think) is doing it through a proxy (php code) but I would prefer not to do it as it impacts the performance.

Have someone knows of an alternative/fix to this problem?

Note: Updating IE8 is not an option as I want to test it without updates.

A very similar problem to mine: http://forum.jquery.com/topic/jquery-ui-tabs-ie8-and-injecting-elements-into-dom

A: 

That would be quite an accomplishment to get this code working on all those browsers you mentioned, if domain2.tld is not on the same server than your javascript.

But I guess I don't really get you. The same origin policy should (and does!) work pretty good (too good maybe) in all major browsers.

jAndy
A: 

I'm gonna have to agree with jAndy here, it's very unlikely you got it working at all. The same origin policy applies (http://en.wikipedia.org/wiki/Same_origin_policy), your best bet is to write a local proxy which your script polls, and then let the proxy do the cross-domain calls.

nillls
+2  A: 

I'm sorry if my English is not perfect, as I can see I was not clear enough... One of my main concerns is explained by other person here: http://http://forum.jquery.com/topic/cross-domain-ajax-and-ie

So, what alternative exists?

1) XDomainRequest

Personally I think this is the best way to implement cross-site scripting in IE8+ (as it is supported natively). The only problem is that is a Microsoft-only way. But as many other things with IE family we can easily extend JQuery ajax functionality.

According to the documentation you will need to specify some additional headers in domain1.tld, for example, in PHP like this:

header("Access-Control-Allow-Origin: http://domain2.tld"); //use * for any

Maybe the next alternative is useful to provide the jquery implementation of XDomainRequest;

Update(a): There is a XDR library (non-jquery) which "replace" the XHR class to make it cross-browser, it is based in pmxdr client library. I haven't try it yet.

2) CORS

The only problem with this plugin is that is not exactly an extension as its functions are named differently, so you need to either change your codes or wrap that plugin.

Update(b): I modified the CORS plugin to make it easier. Check my other answer to get the code.

3) JsonP in JQuery

This should be the easiest way to solve my problem (as I have control of both servers). Natively most browsers supports cross site scripting, only if json format is used (I believe xml can be used as well). In this case $.getJSON() function is used. In order to make it work, you need to specify (as the documentation states) callback=? in the URL, for example:

$.getJSON("http://domain2.tld/index.php?callback=?",funciton(res){ ... }); 

The "?" after "callback" will be replaced with an identifier... in your php file, you need to get that identifier and surround the Json code like this:

print_r($_GET["callback"])."(".json_encode($mydata).");";

(I got that example from here)

The problem with this method is that if you want to retrieve HTML only, it must reside inside a json object and thus making the process a little bit more complicated and overwhelming.

4) jquery.jsonp plugin

If you need extra validations and features to the native JSONP support in JQuery, then try this plugin which will also simplify the process.

5) xdomainajax

This plugin uses an interesting aproach using Yahoo's service YQL, in which any web page (or a part of it) can be converted to Json, making it possible to import it into javascript. This method is good for those situations in which you can not change the origin format.

6) flXHR

This solution uses flash (swf) to achieve the magic. I could say that this is a very quick way to achieve almost a totally cross-browser implementation (as it relies in flash support). This method may be ideal for those sites in which flash will be present (for sure). However if your site does not requires flash, then that become the main disadvantage as users should have flash installed in order this to work.

7) xdajax

This solution is based in YUI implementation together with the "Flash" approach.

8) If none of the previous options is good for you, remember that you can still use the old trick of inserting a tag to import JS code.

9) Lowering IE security to the minimum also solve the problem. But I think it won't be good to have a message like this: "Please lower your security settings in order to use this site"... lol

I hope this can help others in a similar situation.

lepe
+1  A: 

(I recommend to check the list I post before this answer)

To facilitate the process, I took the CORS plugin and modified it. You won't need to modify your existing Jquery code if you just use $.get and $.post. I tested it in IE8 and it works as expected. For the rest of the browsers will use the normal JQuery calls. You can even add this code on-demand (with conditional tags). Read the initial comments for more information. I hope it helps...

Here is the code (save it for example as jquery.xdomain.js):

/*
 * XDR (non-XHR) extension functions for IE8+
 * Based in CORS plugin (http://plugins.jquery.com/project/cors)
 * Modified by A.Lepe (www.alepe.com, Aug 2010)
 * 
 * It supports calls using $.get and $.post only.
 * The main difference between the CORS plugin and this one are:
 *
 * 1) This method tries first to use XDR and if not available 
 * will try to use XHR. This is to prevent any alert or 
 * security message from IE.
 *
 * 2) To minimize size and target only IE8+ versions, this method
 * does not provides an alternative fall-back.
 * CORS version uses proxy_xmlhttp.js as fall-back option (see link #1 below).
 *  
 * If you want to support "OLD" browsers, an alternative fall-back 
 * can be easily implemented (instead the error alert).
 * For example, something like:
 *
 * ...
 * } catch(e) {
 *    data["proxy_url"] = url;
 *    $._get(proxy, data, callback, type);
 * }
 * ...
 *
 * in which "proxy" must be a URL where your proxy is located. 
 * Your proxy may look like:
 *
 * <?php
     //GET method example:
     $URL = $_GET["proxy_url"];
     unset($_GET["proxy_url"]);
     $params = http_build_query($_GET);
     echo file_get_contents($URL."?".$params));
 * ?>
 *
 * For POST method you may check link #2.
 *
 * NOTES:
 *
 * $.post() method it might not work as expected. XDR does
 * not send the data to the server in the same way XHR do
 * (am I missing something?). In order to have access to that
 * POST data you will need to:
 *
 * a) set:
 *      always_populate_raw_post_data = On
 *      register_long_arrays = On
 *
 * OR :
 *
 * b) import it manually (see link #3): 
    //-------- Import XDR POST data ---------
    if (isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
        $data = explode('&', $GLOBALS["HTTP_RAW_POST_DATA"]);
        foreach ($data as $val) {
            if (!empty($val)) {
                list($key, $value) = explode('=', $val);
                $_POST[$key] = urldecode($value);
            }
        }
    }
 *
 * Remember to add the respective headers in the server that will
 * allow the XDR calls:
      header('Access-Control-Allow-Origin: *'); //Or http://example.com
      header('Access-Control-Max-Age: 3628800');
      header('Access-Control-Allow-Methods: GET, POST');
 */
(function($) {
$._get = $.get;
$._post = $.post;
$.get = function (url, data, callback, type) {
    // try XDR
    if (jQuery.browser.msie && window.XDomainRequest) {
        var params = '';
        for (var key in data) {
            params += ((params || url.indexOf("?") != -1)?'&':'?')+key+'='+data[key];
        }
        // Use Microsoft XDR
        var xdr = new XDomainRequest();
        xdr.open("GET", url+params);
        xdr.onload = function() {
            callback(this.responseText, 'success');
        };
        xdr.send();
    } else {
        try {
            // Try using jQuery to get data
            $._get(url, data, callback, type);
        } catch(e) {
            alert(e.message);
        }
    }
}
$.post = function (url, data, callback, type) {
    // Try XDR
    if (jQuery.browser.msie && window.XDomainRequest) {
        var params = '';
        for (var key in data) {
            params += (params?'&':'')+key+'='+data[key];
        }
        // Use XDR
        var xdr = new XDomainRequest();
        xdr.open("POST", url);
        xdr.send(params);
        xdr.onload = function() {
            callback(xdr.responseText, 'success');
        };
    } else {
        try {
            // Try using jQuery to POST
            $._post(url, data, callback, type);
        } catch(e) {
            alert(e.message);
        }
    }
}
})(jQuery);

Links:

lepe