问题
Background Information
I am working on a web application that utilizes GWT (v2.4). For the application, I am creating an iframe that will display some information from another website. I need to access some information from that iframe that is normally restricted via the Same Origin Policy (SOP). However, both sites (the parent and iframe) are hosted on the same super-domain, just under different sub-domains. So, something like this:
- Parent: dev.app.mySite.com
- frame: someOtherContent.mySite.com
I know the usual solution for this problem is to set the property: document.domain = 'mySite.com' on both parent and iframe site to allow passage of SOP. This works for all browsers (that I'm concerned with) except Internet Explorer 8 (and probably other versions).
The Problem
In IE, when I attempt to load my web application, I get a completely blank page with the following JS exception, "Access is denied." The source of this problem is in GWT's myGwtAppName.nochache.js where GWT generates some code during the compilation process that it needs (see below).
From the research I've done on this problem in general, the root cause of this issue seems to be that in IE, unlike all other browsers, iframes don't inherit their parent's document.domain settings. From what I understand, the code generated by GWT runs in an iframe (based on this comment: https://stackoverflow.com/a/5161888). So, what I think is happening based on my limited knowledge of JS:
- I set document.domain = 'mySite.com' in the parent index page via JS and it is processed.
- myGwtAppName.nochache.js is processed.
- In nochache.js, code is ran to setup the GWT iframe sand-box environment
- In that code, a call is being made to a SOP restricted property of the sand-box iframe
- An exception is thrown because the site's parent document domain has been set to 'mySite.com' and the iframe's document.domain doesn't inherit that setting, so it's still 'dev.app.mySite.com'. This won't pass SOP because the domain has to be exactly the same.
The generated code that causes the exception
The below code, looks like it's setting up the GWT sandbox iframe environment.
var $intern_4 = 'myGwtAppName',
$intern_7 = 'body',
$intern_8 = 'iframe',
$intern_9 = 'javascript:""',
$intern_10 = 'position:absolute; width:0; height:0; border:none; left: -1000px; top: -1000px; !important',
$intern_11 = '<html><head><\/head><body><\/body><\/html>',
$intern_12 = 'script',
$intern_13 = 'javascript',
$intern_14 = 'var $wnd = window.parent;''
....
....
function setupInstallLocation(){
if (frameDoc) {
return;
}
var scriptFrame = $doc.createElement($intern_8);
scriptFrame.src = $intern_9;
scriptFrame.id = $intern_4;
scriptFrame.style.cssText = $intern_10;
scriptFrame.tabIndex = -1;
$doc.body.appendChild(scriptFrame);
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document; //THIS CAUSES THE EXCEPTION
}
frameDoc.open();
frameDoc.write($intern_11);
frameDoc.close();
var frameDocbody = frameDoc.getElementsByTagName($intern_7)[0];
var script = frameDoc.createElement($intern_12);
script.language = $intern_13;
var temp = $intern_14;
script.text = temp;
frameDocbody.appendChild(script);
}
....
....
My Questions
- Is my analysis of the situation completely off-base?
- Has anyone seen a solution for this problem that will work in a GWT environment in IE?
Information Sources
IE doesn't inherit document.domain settings: https://stackoverflow.com/a/1888711 (and many other threads).
GWT runs in an iframe sand-box environment: https://stackoverflow.com/a/5161888
回答1:
You may use html5 web messaging
to communicate between iframe and parent.
Be aware that Internet Explorer has following bugs. You can send only string as messages. You can't send object like other browser support.
Some people advice to encode object into JSON if you wish to send more then just a string but sometimes it is cheaper to send URL encoded string just like query string in URL.
Here are examples 2 top results from my google http://tutorials.jenkov.com/html5/messaging.html https://thenewcircle.com/bookshelf/html5_tutorial/messaging.html
Take a look that they use different code to listen for messages
window.attachEvent("onmessage", handleMessage);
window.addEventListener("message", handleMessage, true);
First works with IE and old Opera and last works with a rest of world.
回答2:
I've run into the same issue and have found no elegant solution, but...
Right now I have an ant task that manually alters 3 specific points in the GWT nocache.js file after compilation to workaround the issue. You have to use regular expressions to make sure the injected code can reference a couple specific variables in the obfuscated code. It's all terribly ugly...
If you've found a more elegant solution please do post, since my solution is a hack. Details below...
Note - This assumes you have compiled GWT in "PRETTY" mode, since that's the only way I could reference variable/method names.
The problem we really need to solve here is that IE does not inherit altered document.domain values. So IFrames will have invalid document.domains. You can, however, force an IFrame to set its own document.domain, so that it is in sync with the outer page. However - this method requires letting the IFrame load and execute first. Which means that further operations must be executed in a callback after the iframe has loaded.
1) You need to add the follow two methods to the gwt .js file:
function waitForSetupInstallLocation(callback){
if (frameDoc) {
return;
}
var scriptFrame = $doc_0.createElement('iframe');
scriptFrame.src="javascript:document.open();document.domain='<domainvalue>';document.close();document.body.contentEditable=true;";
scriptFrame.id = '<MyWidgetSetName>';
scriptFrame.style.cssText = 'position:absolute; width:0; height:0; border:none; left: -1000px;' + ' top: -1000px;';
scriptFrame.tabIndex = -1;
$doc_0.body.appendChild(scriptFrame);
var addedContent = false;
try {
setFrameDoc(scriptFrame);
callback();
}
catch(e){
scriptFrame.onload = function(){
if(!addedContent){
addedContent = true;
setFrameDoc(scriptFrame);
callback();
}
};
}
}
function setFrameDoc(scriptFrame){
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document;
}
frameDoc.open();
var doctype = document.compatMode == 'CSS1Compat'?'<!doctype html>':'';
frameDoc.write(doctype + '<html><head><\/head><body><\/body><\/html>');
frameDoc.close();
}
These two methods allow GWT to inject code into the page while also waiting for IE to load an IFRAME which then changes its own document.domain. You can see that the first accepts a callback. The callback is executed only after the IFrame is loaded.
The next issue is that these are asynchronous methods, and only accept callbacks. GWT currently does all setup synchronously. So the next modification is that the two methods that need to use it must be altered. All of the inner content of the following methods:
function installCode(code_0){...}
<WidgetSetName>.__installRunAsyncCode = function(code_0){...}
Needs to be wrapped in a function, and passed to the waitForSetupInstallLocation method as a callback. So that essentially you have turned those methods into asynchronous methods.
An example of what this looks like is:
function installCode(code_0){
waitForSetupInstallLocation(function(){
<real method content>
});
}
Once you have done all this - it should work in IE, and should remain functional in other browsers, since youve only added the use of a callback.
来源:https://stackoverflow.com/questions/21056559/gwt-access-is-denied-javascript-error-when-setting-document-domain-in-intern