无意中用IE9打开了邮箱项目(凡科邮箱),点击写信,发现富文本编辑器出不来,报错了。最后发现是跨域导致,IE10和9都会,这两个核对于iframe的同源判定策略有点坑爹,不按套路来。先说问题和解决方法。
问题: 报错如下,动态创建iframe后,获取其document时“拒绝访问”,应该就是跨域了。
分析: 这个iframe只是个容器,没有给它src(所以iframe的document.domain可能有问题)。跨域最先想到的就是给iframe的document.domain设置和父页面相同。
然而这里获取其document就已经被拒绝,根本无法通过document.domain设置。
解决: 用一种“旁门左道”来解决。你不是没有src嘛?又不能随便给你设置src(iframe会发生跳转,内容改变),那就通过src让你执行一下js来设置domain,代码如下:
iframe.src = "javascript:void((function(){document.open();document.domain='"+ document.domain + "';document.close()})())";
深入探讨一下:
IE10/9为什么会有这个问题,听说某次更新之前还是正常的。和IE坑爹的同源识别策略有关(应该说是一个bug)。
其他现代浏览器没问题,是因为对于本窗口动态创建的iframe,无src的情况下,毕竟是自己创建的,会将他识别为同源,没有安全问题。IE10/9在正常情况下也没问题(以前都能正常使用的),即,没有在父窗口设置过domain,就没有问题。一旦在父窗口设置过domain,这时再创建iframe且没有给src,就被认为跨域了。即使是document.domain = document.domain; 这时domain是没有变化的,但在IE里面还是会被认为跨域了,只能对该iframe用上面的javascript方法设置相同的domain才行。
所以mail的写信页面报错,应该是某一次更新代码时,在父页面对document.domain进行了设置(即使是document.domain = document.domain;)影响到了。
下面可以进行验证。用以下代码进行测试:
//document.domain = document.domain; //父窗口设置domain,实际上domain是不变的
var ifr = document.createElement('iframe'); //创建iframe
document.body.appendChild(ifr);
//ifr.src = "javascript:void((function(){document.open();document.domain='"+ document.domain + "';document.close()})())"; //通过iframe.src设置其domain
var idoc = ifr.contentWindow.document; //获取iframe的document看看是否会“拒绝访问”
console.log(idoc.domain, document.domain);
chrome这样的标准浏览器没问题,上面代码随便玩。在IE10测试如下:
1.这是正常的纯净页面,父窗口没有设置过document.domain。创建出来的iframe能正常操作。
2.页面刷新一下,重新来。父页面加上document.domain = document.domain;
3.把ifr.src = "javascript:xxx…"加上,又正常了。
说到底还是IE10/9的同源识别方式有问题,随便挑拨一下(document.domain = document.domain;),就不认自己的崽。
珍爱生命,远离IE。