代码改变世界

从IE的一个奇怪的现象所联想到的(上)

2009-11-26 20:08  Nana's Lich  阅读(3353)  评论(10编辑  收藏  举报

消歧义声明:因为上下文关系的缘故,JScript特指微软的JScript引擎以及其语言特点,但大部分时候可以在功能上视为JavaScript的等价物;JavaScript指一般意义的JavaScript。

 

大概有一年左右了吧,开着IE8浏览一些网站以后,我就会碰到IE8的界面失灵的现象,刚开始我还以为是32位的IE和64位的Windows有兼容问题。

上个月我装了32位的Windows 7,没过几天又发现了这个问题,所以我就把这种现象报告给了微软的技术支持。

微软的技术支持回信:在他们的测试中IE8不存在这种问题,根据他们以往研究经验,这种问题可能是由第三方加载项和组件造成的。

这一下倒是提醒了我,我总是装完操作系统以后马上就安装各种常用工具,可能是某个常用工具出现了问题。

仔细排查之后,我把目标锁定在了这两样东西上:NetTransport BHO和Flash Player。

NetTransport也就是国产的网络传送带,算是国产下载工具里面最干净的了,而我以前私下分析过它的原理,觉得它没什么可能产生这样的问题。

相比之下,Flash Player就显得很有嫌疑了,Flash Player本来就给我留下过不好的印象——怎么回事,马上就说。

之后的几天我就很少去那些在页面上放置一大堆Flash内容(尤其是Flash封装组件)的网站——说来也奇怪,到现在已经5天了,再没有出现过这样的问题。

不过这也不能说问题就是出在Flash Player身上,万一那些网站上正好有Flash以外的真正的罪魁祸首呢?

但是这次经历却再次提醒了我,的确应该小心对待Flash Player(ActiveX)。

 

2007年下半年,我为朋友做一个小小的Web程序,当时我用了ASP.NET和Flash……但在刚开始测试就发现有个严重问题,那就是:在ASP.NET WebForm上放置的Flash封装组件,无法调用!

只有在IE上才会这样,现在大家可能都知道怎么解决了,但当时我不知道,所以我就看弹出来的脚本调试器怎么说——

结论是,问题出在Flash Player对外部方法的实现上。

 

和它在Firefox中对XPCOM的实现不一样,Flash Player的ActiveX实现(用于Windows应用程序,包括IE)并没有好好利用ActiveX/OLE/Automation的技术基础——IDispatch接口,而是只暴露了CallFunction方法,再在盛装它的容器上运行一段JScript脚本。

这段JScript脚本需要把Flash Player控件自身当作参数,然而Flash Player用来寻找自身的办法就很有问题了——当时它使用了这样几个片段:

Code

在运行时Flash Player会在这三个片段之间填入自身的id,也就是变成下边这样:

__flash__addCallback(idOfFlashPlayer, "idOfFlashPlayer");

Flash Player(ActiveX)这样设计,是建立在“在IE中使用id和name可以直接访问一个节点”的前提之下的,但Macromedia或者Adobe的人其实是考虑不周——IE的这个特点不仅不是标准功能,连微软的技术文档都没保证过在任何情况下都可以这么用。

实际上,上面这个脚本错误的产生原因就是:在form内的节点是不属于全局作用域的——也就是说,在这个时候IdOfFlashPlayer、window.idOfFlashPlayer,都无效。

 

不光这样,就算可以直接用id找到form内的节点,万一id中有不能用作JScript标识符的字符,这种做法也是要出错的,比方说:

__flash__addCallback(id-of-flash-player, "id-of-flash-player");
像这个时候,id里当作分隔符的减号就会起到它在JScript中本来的意义——减法。自然的,无论是id,还是of、flash、player都不是我们要用的,id - of - flash - player也没有实际意义。
上面这个问题是旧版本的Flash Player(ActiveX)才有的,据我所知新版本的Flash Player(ActiveX)已经没这个问题了。
 
但是即使这样改动了,Flash Player(ActiveX)这种不靠自己维护而靠脚本代劳的做法,仍然让人觉得不安——这个__flash__addCallback函数的内部构造是这样的:
function __flash__addCallback(instance, name) {
  instance[name] 
= function () { 
    
return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0+ "</invoke>"));
  }
}
这实在是一个让人看了之后觉得很不放心的做法……虽然Flash Player另外还有一个__flash__removeCallback的函数,但我始终是会担心在什么地方给我捅出篓子——当然,还是要再次强调,这种担心基本上是多余的。
 
昏昏欲睡的头脑,努力把两天前的稿子补完也好像哪里不太对劲……至于说到底为什么让人担心,这就是Microsoft COM技术的问题了,今天就先到这里吧。