GWT - 仅在 Internet Explorer 中设置 document.domain 时出现"Access is Denied" JavaScript 错误



背景信息

我正在开发一个使用GWT(v2.4)的web应用程序。对于该应用程序,我正在创建一个iframe,它将显示来自另一个网站的一些信息。我需要访问iframe中的一些信息,这些信息通常通过同源策略(SOP)进行限制。然而,两个站点(父站点和iframe)都托管在同一个超级域上,只是位于不同的子域下。所以,像这样的东西:

  • 父级:dev.app.mySite.com
  • 框架:someOtherContent.mySite.com

我知道这个问题的通常解决方案是在父站点和iframe站点上设置属性:document.domain='mySite.com',以允许SOP通过。这适用于除InternetExplorer8(可能还有其他版本)之外的所有浏览器(我关心的)。

问题

在IE中,当我尝试加载web应用程序时,我会得到一个完全空白的页面,并出现以下JS异常:"拒绝访问"。这个问题的根源是GWT的myGwtAppName.nochache.JS,GWT在编译过程中生成了一些所需的代码(见下文)。

从我对这个问题所做的研究来看,这个问题的根本原因似乎是在IE中,与所有其他浏览器不同,iframe不会继承其父级的document.domain设置。据我所知,GWT生成的代码在iframe中运行(基于以下注释:https://stackoverflow.com/a/5161888)。因此,基于我对JS的有限了解,我认为正在发生的事情:

  1. 我通过JS在父索引页中设置document.domain='mySite.com',并对其进行处理
  2. myGwtAppName.nochache.js被处理
  3. 在nochache.js中,运行代码来设置GWT iframe沙盒环境
  4. 在该代码中,正在对砂箱iframe的SOP限制属性进行调用
  5. 引发异常,因为该网站的父文档域已设置为"mySite.com",而iframe的document.domain未继承该设置,因此它仍然是"dev.app.mySite.com'"。这不会通过SOP,因为域必须完全相同

导致异常的生成代码

下面的代码看起来像是在设置GWT沙箱iframe环境。

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);
}
....
....

我的问题

  1. 我对形势的分析完全不准确吗
  2. 有人见过在IE中的GWT环境中可以解决这个问题的解决方案吗

信息源

IE不继承文档.domain设置:https://stackoverflow.com/a/1888711(以及许多其他线程)。

GWT在iframe沙盒环境中运行:https://stackoverflow.com/a/5161888

您可以使用html5 web messaging在iframe和父级之间进行通信。

请注意,Internet Explorer存在以下错误。您只能将字符串作为消息发送。您不能像其他浏览器支持那样发送对象。

有些人建议,如果您希望发送更多而不仅仅是一个字符串,则将对象编码为JSON,但有时发送URL编码的字符串会更便宜,就像URL中的查询字符串一样。

以下是我的谷歌搜索的两个热门结果http://tutorials.jenkov.com/html5/messaging.htmlhttps://thenewcircle.com/bookshelf/html5_tutorial/messaging.html

看看他们使用不同的代码来监听消息

window.attachEvent("onmessage", handleMessage);
window.addEventListener("message", handleMessage, true);

第一部作品与IE和旧Opera合作,最后一部作品与世界其他地方合作。

我遇到了同样的问题,没有找到优雅的解决方案,但。。。

现在,我有一个ant任务,它在编译后手动更改GWTNocache.js文件中的3个特定点,以解决这个问题。您必须使用正则表达式来确保注入的代码可以引用模糊代码中的几个特定变量。这一切都非常丑陋。。。

如果你找到了一个更优雅的解决方案发帖,因为我的解决方案是破解版。以下详细信息。。。

注意——这假设您已经在"PRETTY"模式下编译了GWT,因为这是我引用变量/方法名称的唯一方式。

我们真正需要解决的问题是IE不会继承更改后的document.domain值。因此,IFrame将具有无效的document.domain。但是,可以强制IFrame设置其自己的document.domain,使其与外部页面同步。然而,此方法需要让IFrame首先加载并执行。这意味着在加载iframe之后,必须在回调中执行进一步的操作。

1) 您需要在gwt.js文件中添加以下两种方法:

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();
}

这两种方法允许GWT在等待IE加载IFRAME的同时向页面中注入代码,然后IFRAME会更改自己的document.domain。只有在加载IFrame之后才执行回调。

下一个问题是,这些都是异步方法,并且只接受回调。GWT目前同步完成所有设置。因此,下一步的修改是必须更改需要使用它的两种方法。以下方法的所有内部内容:

function installCode(code_0){...}
<WidgetSetName>.__installRunAsyncCode = function(code_0){...}

需要封装在函数中,并作为回调传递给waitForSetupInstallLocation方法。因此,从本质上讲,您已经将这些方法转换为异步方法。

这种情况的一个例子是:

function installCode(code_0){
waitForSetupInstallLocation(function(){
<real method content>
});
}

一旦你完成了所有这些,它应该在IE中工作,并且应该在其他浏览器中保持功能,因为你只添加了回调的使用。

相关内容

最新更新