I'm developing a JavaScript application that's meant to be run either from a web server (over http) or from the file system (on a file:// URL).
As part of this code, I need to use XMLHttpRequest to load files in the same directory as the page and in subdirectories of the page.
This code works fine ("PASS") when executed on a web server, but doesn't work ("FAIL") in Internet Explorer 8 when run off the file system:
<html><head>
<script>
window.onload = function() {
var xhr = new XMLHttpRequest();
xhr.open("GET", window.location.href, false);
xhr.send(null);
if (/TestString/.test(xhr.responseText)) {
document.body.innerHTML="<p>PASS</p>";
}
}
</script>
<body><p>FAIL</p></body>
Of course, at first it fails because no scripts can run at all on the file system; the user is prompted a yellow bar, warning that "To help protect your security, Internet Explorer has restricted this webpage from running scripts or ActiveX controls that could access your computer."
But even once I click on the bar and "Allow Blocked Content" the page still fails; I get an "Access is Denied" error on the xhr.open call.
This puzzles me, because MSDN says that "For development purposes, the file:// protocol is allowed from the Local Machine zone." This local file should be part of the Local Machine Zone, right?
How can I get code like this to work? I'm fine with prompting the user with security warnings; I'm not OK with forcing them to turn off security in the control panel.
EDIT: I am not, in fact, loading an XML document in my case; I'm loading a plain text file (.txt).
Hmm, could it be the difference between the native XMLHttpRequest object and the ActiveX one? I seem to remember something about that. That is, instead of
var xhr = new XMLHttpRequest();
try
var xhr = new ActiveXObject("MSXML2.XMLHTTP");
Obviously, put some checks in place to see if the browser supports ActiveX. Of course, this is limited to IE only, as well.
I just happened to stumble across exactly the same problem. As suggested above, the non-native ActiveX "constructor" works. I’m not really sure whether there are different policies applied to the two objects, but since jQuery mentions the same problem as well, it may be a genuine bug. Here is the relevant piece of code from the jQuery source (1.4.2, line 4948):
// Create the request object; Microsoft failed to properly
// implement the XMLHttpRequest in IE7 (can't request local files),
// so we use the ActiveXObject when it is available
// This function can be overriden by calling jQuery.ajaxSetup
xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
function() {
return new window.XMLHttpRequest();
} :
function() {
try {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {}
}
How can I get code like this to work?
As suggested above, this looks like a fault in Microsoft XMLHttpRequest
. jQuery (Jul 2011) also writes:-
Microsoft failed to properly implement the XMLHttpRequest in IE7 (can't request local files)
I confirm this failure for IE8 too.
A solution is to use new window.ActiveXObject( "Microsoft.XMLHTTP" )
for local files if XMLHttpRequest
doesn't work.
The failure is in the xhr.open
line so it can be caught there and then try ActiveXObject
as follows:-
var xhr = new XMLHttpRequest()
try {
xhr.open('GET', url, true)
}
catch(e) {
try {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
xhr.open('GET', url, true)
}
catch (e1) {
throw new Error("Exception during GET request: " + e1)
}
}
This code will at least use standard XMLHttpRequest
for IE9 (untested) and future IE browsers if/when Microsoft fixes the fault. With the jQuery
code above, non standard Microsoft.XMLHTTP
will be used whenever ActiveXObject
is available, even if Microsoft fix the fault.
I know this is old - but wanted to clarify what is going on with a little more detail, and offer a couple of potential options.
First off this is actually not a flaw within IE, but a security feature that exists in Chrome as well.
Basically, any resource URI with file:// prefix is not allowed to load any other resource URI with file:// prefix using XMLHttpRequest.
In IE you will see an Access Denied message. In Chrome you will see "Failed to load resource: Origin null is not allowed by Access-Control-Allow-Origin"
More info -> Information on IE and Information on Chrome (look for --allow-file-access-from-files)
An interesting thing with IE is that if you are using the .NET Browser
Control inside of a WinForm or Silverlight App, this feature is
disabled, and you will not have the same issues.
There are a couple of workarounds that I am aware of - none of which are ideal, as they disable a measure of security protection
- IE: The first, and most "benign", is that you can add the URI of the calling page to the Trusted Sites zone (Uncheck "Enable Protected Mode")
- IE: following the link above there is a registry setting that you can modify which will disable this feature - but this would have to be done on each machine trying to load the resources
- Chrome: The link above refers to using the command line switch when starting Chrome to disable the feature.
Once again, this is a security feature within browsers to mitigate potential threat vectors - similar to the Cross Domain scripting blocks.