GWT - 仅在Internet Explorer中设置document.domain时出现“访问被拒绝”JavaScript错误

时间:2014-01-10 23:46:34

标签: javascript gwt iframe subdomain

背景资料

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

  • 家长:dev.app.mySite.com
  • frame:someOtherContent.mySite.com

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

问题

在IE中,当我尝试加载我的Web应用程序时,我得到一个完全空白的页面,其中包含以下JS异常,“访问被拒绝。”这个问题的根源在于GWT的myGwtAppName.nochache.js,其中GWT在编译过程中生成了一些代码(见下文)。

从我对这个问题的研究总体而言,这个问题的根本原因似乎是在IE中,与所有其他浏览器不同,iframe 继承其父文档。域名设置。根据我的理解,GWT生成的代码在iframe中运行(基于此评论:https://stackoverflow.com/a/5161888)。所以,基于我对JS的有限知识,我认为正在发生的事情:

  1. 我通过JS在父索引页面中设置document.domain ='mySite.com'并进行处理。
  2. myGwtAppName.nochache.js已处理。
  3. 在nochache.js中,运行代码以设置GWT iframe沙箱环境
  4. 在该代码中,正在调用sand-box iframe的SOP限制属性
  5. 引发异常是因为网站的父文档域已设置为“mySite.com”且iframe的document.domain未继承该设置,因此它仍然是“dev.app.mySite.com”。这不会通过SOP,因为域必须完全相同。
  6. 导致异常的生成代码

    以下代码看起来像是在设置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环境中使用?
    3. 信息来源

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

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

2 个答案:

答案 0 :(得分:4)

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

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

有些人建议将对象编码为JSON,如果你想发送更多信息只是一个字符串,但有时发送URL编码的字符串比在URL中查询字符串更便宜。

以下是我谷歌的2个最佳结果示例 http://tutorials.jenkov.com/html5/messaging.html https://thenewcircle.com/bookshelf/html5_tutorial/messaging.html

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

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

首先使用IE和旧Opera,最后与世界其他地方合作。

答案 1 :(得分:0)

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

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

如果您找到了更优雅的解决方案 ,请 发帖,因为我的解决方案是黑客攻击。详情如下......

注意 - 这假设你已经在&#34; PRETTY&#34;中编译了GWT。模式,因为这是我可以引用变量/方法名称的唯一方法。

我们真正需要解决的问题是IE不会继承更改的document.domain值。因此,IFrame将包含无效的document.domains。但可以强制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,然后更改自己的document.domain。你可以看到第一个接受回调。 加载IFrame后,回调仅在中执行。

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

function installCode(code_0){...}

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

需要包装在函数中,并作为回调传递给waitForSetupInstallLocation方法。所以基本上你已经把这些方法变成了异步方法。

这看起来是一个例子:

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

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