IE控件在flash跨域时回调接口无效的问题研究

问题是这样的,我们公司的产品《UU页游戏助手》中是使用了IE浏览器的ActiveX控件来运行网页游戏,其中创建代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
hr = ::CoCreateInstance("{8856F961-340A-11D0-A96B-00C04FD705A2}", NULL, CLSCTX_ALL, IID_IOleControl, (LPVOID*)&pOleControl);
GAMEMGR_ASSERT(SUCCEEDED(hr));
if (FAILED(hr) || pOleControl == NULL)
{
return false;
}
pOleControl->QueryInterface(IID_IOleObject, (LPVOID*) &m_pUnk);
pOleControl->Release();
if (m_pUnk == NULL)
{
return false;
}
// Create the host too
m_pControl = new GMActiveXCtrl();
m_pControl->SetOwner(this);
// More control creation stuff
DWORD dwMiscStatus = 0;
m_pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
IOleClientSite* pOleClientSite = NULL;
m_pControl->QueryInterface(IID_IOleClientSite, (LPVOID*) &pOleClientSite);
GMSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
// Initialize control
if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) != 0)
{
m_pUnk->SetClientSite(pOleClientSite);
}
IPersistStreamInit* pPersistStreamInit = NULL;
m_pUnk->QueryInterface(IID_IPersistStreamInit, (LPVOID*) &pPersistStreamInit);
if (pPersistStreamInit != NULL)
{
hr = pPersistStreamInit->InitNew();
pPersistStreamInit->Release();
}
if (FAILED(hr))
{
return false;
}
if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) == 0)
{
m_pUnk->SetClientSite(pOleClientSite);
}

在使用这个IE控件时,经常有诸多问题,很多行为和IE不一致。需要去注册表添加不同的选项,最经典和常见的问题莫过于IE兼容性的问题,需要在HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION中添加注册表键来强行指定兼容版本。

问题来了

然而 在前不久和今天都有客户反映他们的 flash广告页面(是那种打开后是个flash页面,然后 点击flash里的按钮/图像就弹出注册登录的 html小浮层的页面)在我们的浏览器上有问题,用户第一次打开页面能正常弹出小浮层,但后面再打开就不显示了。

第一次粗略的研究下,发现清除ie缓存后再打开页面后表现正常,经过调试js也没有发现明显的问题,就让客户在请求swf的链接上添加?t=当前时间戳的方式避免缓存。但今天再遇到了,就想彻底的研究下到底在什么情况下会出现这个问题。

使用vs2015使用 脚本模式 附加到我们的程序上,打开解决方案管理器进行调试。多次跟踪代码和对比发现在html这边初使化flash的流程是一样的,所以问题不是在这边。而初使化之后,无缓存的情况下点击flash,js中的函数能被调用,而有缓存的情况下点击flash,js中的函数无法被调用。

在无缓存的情况下,在js函数中下断点,然后回溯堆栈,发现上层调用者是动态生成的代码”eval code”:

1
try { __flash__toXML(sayHello(({code:1,description:"Parameter test"}))) ; } catch (e) { "<undefined/>"; }

因为有过一段as的编码经验,所以知道这是ie和flash生成的支持swf文件里的action script调用js的函数,那么问题来了,为什么在有缓存的情况下没有自动生成这些代码?

堆栈截图

模拟重现问题

由于原网页过于复杂,我首先想将这个问题用简单的代码复现,就找到了以前用来测试actionscript和js交互时制作的demo,将其直接布署在我的本地测试机器上。

  1. 我以ip访问测试页面,多次刷新页面,再点击flash按钮,可以正常工作。
  2. 对比发现广告页面是动态插入的flash,于是将demo改成动态插入,但还是无异常。
  3. 绑定个域名到本机,然后使用域名访问,多次刷新后点击,可以正常工作。
  4. 将swf放在另一个域名,然后多次刷新后点击,不工作。但是这种情况,是无论有无缓存,都不正常工作。
  5. 对比发现demo中的flash节点中allowScriptAccess="sameDomain",而网页的节点属性是allowScriptAccess="always",所以是flash属性导致的。
  6. 去除该属性后再多次刷新对比,成功模拟环境。

IE控件中打开的页面中,如果是从 缓存中加载的swf文件,在 跨域的情况下 未能正常生成flash的相关 动态js代码,导致flash中的actionscript不能调用javascript函数。

但是,在ie浏览器中,无论是否从缓存中加载swf,均能正常生成动态js代码。

解决问题

尝试使用跨域文件,无效。

未能解决此问题,留待以后继续研究吧。。

参考: