【问题标题】:GWT - "Access is Denied" JavaScript error when setting document.domain in Internet Explorer onlyGWT - 仅在 Internet Explorer 中设置 document.domain 时出现“拒绝访问”JavaScript 错误
【发布时间】:2014-01-30 03:34:19
【问题描述】:

背景信息

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

  • 父级:dev.app.mySite.com
  • 框架: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. 在该代码中,正在调用沙盒 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 不继承 document.domain 设置:https://stackoverflow.com/a/1888711(以及许多其他线程)。

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

【问题讨论】:

    标签: javascript gwt iframe subdomain


    【解决方案1】:

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

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

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

    以下是来自我的 google 的 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 合作,最后与世界其他地方合作。

    【讨论】:

    • 感谢您的回答!如果我能让 document.domain 工作正常,这绝对是我一直在考虑的替代解决方案。但是,在我用尽当前的追求之前,我想避免使用跨域消息传递。
    【解决方案2】:

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

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

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

    注意 - 这假设您已在“PRETTY”模式下编译 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 中工作,并且应该在其他浏览器中保持功能,因为你只添加了回调的使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-12
      • 2013-02-06
      • 1970-01-01
      • 2015-03-19
      • 2019-12-19
      • 2015-04-02
      • 2019-09-12
      相关资源
      最近更新 更多