冯博文 汲如意 于佳耕
1(中国科学院大学 北京 100190)
2(中国科学院软件研究所互联网软件技术实验室 北京 100190)
Linux中浏览器兼容ActiveX控件机制的设计与实现
冯博文1,2汲如意1,2于佳耕2
1(中国科学院大学 北京 100190)
2(中国科学院软件研究所互联网软件技术实验室 北京 100190)
目前,浏览器已经成为计算机系统中不可缺少的组成部分。然而,全球市场份额最高的Internet Explorer浏览器,其广泛应用的ActiveX控件却无法运行在Linux操作系统的浏览器中。因此,研究了两种浏览器插件框架,NPAPI和ActiveX,提出了一种兼容机制。该机制为中间件解决方案,一端面向NPAPI,另一端面向Wine中经封装的ActiveX控件,实现NPAPI和ActiveX控件间的消息传输和转换。实验结果表明,兼容机制实现了ActiveX控件运行在Linux操作系统的浏览器中,且加载时间、运行效率和内存占用均在可接受的范围内,为ActiveX控件跨平台技术提供了一种新的方法。
ActiveX控件 跨平台 Linux NPAPI WebKit
浏览器为在因特网检索、呈现和浏览信息的一种软件,它可以显示网页内容并允许用户与之交互。浏览器插件则为本地二进制代码,当浏览器自身的功能无法满足网页需求时,浏览器将加载插件,并借助插件实现的功能。目前,浏览器作为因特网世界的入口,已经成为各大软件厂商的必争之地。其中,Internet Explorer浏览器(简称IE浏览器)在全球的市场份额最高[1]。IE浏览器为微软开发的浏览器,凭借Windows操作系统的垄断地位,在国内的电子支付、在线办公等领域仍然具有重要的地位[2],但至今尚不能运行在Linux操作系统的浏览器中。
随着发行版用户体验的逐步提升,Linux 操作系统在桌面领域也存在大量用户。如果能够使ActiveX控件运行在Linux操作系统的浏览器中,无论对重复利用现有的系统(如网上银行、企事业单位在线办公网站等),节约重复开发的成本,或者对Linux操作系统的推广,都具有重要的现实意义。
目前,针对跨平台运行二进制代码的技术已经存在一些研究成果,主要分为两个方向:系统级虚拟化和库级虚拟化[3-4]。系统级虚拟化即虚拟机技术,指在一台物理计算机上,虚拟出一个或多个逻辑计算机,逻辑计算机相对独立,可以运行不同的操作系统和应用程序,其代表软件为VirtualBox和VMWare等。用户可以在Linux 操作系统中创建Windows操作系统的虚拟机,但虚拟机需要较高的硬件资源,特别需要支持VT-x或AMD-V技术的处理器才能具有较好的效果。仅因为需要浏览含有ActiveX控件的网页而让用户配置启动复杂的虚拟机,十分不方便,并且采用虚拟机仍然没有摆脱Windows操作系统。库级虚拟化的代表为开源项目Wine,Wine为POSIX操作系统中转换Windows API的兼容层[5]。Wine没有模拟一个完整的计算机,而是将二进制代码中对Windows API的调用转换为POSIX调用,同时启动wineserver守护进程模拟Windows内核,为在Wine中的程序提供调度、Windows消息机制等内核功能。若将IE浏览器整体移植至Wine中,技术难度较大。浏览器内核代码量级为百万行级,其复杂度不亚于操作系统内核,且IE浏览器与Windows操作系统高度耦合。开源项目IEs 4 Linux尝试将IE浏览器移植至Wine中,主要面向Web开发者测试网页在IE浏览器中的布局渲染效果,仅支持IE浏览器的旧版本,于08年停止维护。
此外,开源项目FireBreath专门针对跨平台浏览器插件提供了开发框架[6]。若基于FireBreath提供的开发框架,则需要获取ActiveX控件的源代码并做二次开发。然而,在目前的条件下无法获取ActiveX控件的源代码,并且针对ActiveX控件的二次开发代价较大。
采用现有的解决方案具有诸多的缺点,因此,本文提出了一种在Linux操作系统中浏览器兼容ActiveX控件的机制。该机制一端面向NPAPI,一端面向Wine中经封装的ActiveX控件,实现了NPAPI和ActiveX控件间的消息传输和转换,使ActiveX控件运行在Linux操作系统的浏览器中。
网景插件应用程序编程接口NPAPI(Netscape Plugin Application Programming Interface)为网景公司开发的浏览器插件框架,也为大部分非IE浏览器共同支持的插件框架[7]。NPAPI为一个简洁的跨平台开发框架,它定义了几十个函数,分为两组,一组由浏览器提供给插件调用,名称以NPN开头。一组由插件提供给浏览器调用,名称以NP和NPP开头[8]。NPAPI插件在Linux操作系统中为so文件,在网页中的形式如下:
var plugin = document.getElementById(″plugin″);
plugin.foo();
变量plugin为JavaScript可编程对象,与NPAPI的NPObject对象对应,foo函数由插件代码实现。NPObject含有NPClass函数指针表,JavaScript可编程对象的函数调用和数据访问都映射至NPObject。浏览器调用NPHasMethod函数查询foo函数是否存在,若存在,则紧接着调用NPInvoke函数,由NPObject完成对插件代码的调用。数据成员访问流程与函数调用流程相似。同时,NPAPI定义了变体类型NPVariant作为JavaScript数据类型与C++数据类型的映射。
ActiveX为微软推出的组件技术,为OLE(Object Linking and Embedding,对象链接与嵌入)针对互联网的延伸,本质为COM(Component Object Model,组件对象模型)组件[9]。因此,ActiveX控件可以嵌入任何支持COM组件的程序中,但主要应用于互联网,在网页中的形式如下:
classid=″CLSID:6B5FACBE-A8C1-4DCE-8D0F-B8E1C454234A″ codebase=″https://demo.com/control.cab″>
classid为GUID(Globally Unique Identifier,全局唯一标识符),GUID标识了需要加载的ActiveX控件,利用注册表和GUID可以直接找到相应的dll或ocx文件。codebase为ActiveX控件的下载地址。ActiveX控件与COM组件具有完全一样的生命周期,IE浏览器调用Windows API获取控件的IClassFactory指针,然后向IClassFactory指针请求COM对象。所有COM对象都实现了IUnknown接口,ActiveX控件的函数调用和数据成员访问都经IUnknown接口映射至COM对象。
若要实现ActiveX控件运行在Linux操作系统的浏览器中,需要解决三个关键问题:
(1) 浏览器如何获取和识别ActiveX控件标签:服务器可能会识别UA(User Agent,用户代理)直接拒绝不兼容浏览器的访问。同时,NPAPI插件和ActiveX控件在网页中的表现形式不同。
(2) ActiveX控件如何运行在Linux操作系统中:ActiveX控件为Windows操作系统的二进制代码,无法直接运行在Linux操作系统中。同时,我们无法改动ActiveX控件。
(3) 浏览器如何与ActiveX控件交互:浏览器具有NPAPI接口,ActiveX控件具有COM组件接口,需要在其间设计一个合理的消息机制,消除NPAPI和ActiveX控件的差异。
兼容机制采取了中间件的解决方案,即在浏览器与ActiveX控件间构建一个中间件,由AxPlugin和AxLoader两个部分组成,负责传输消息和转换消息。同时,需要在浏览器中实现UA伪装和JavaScript代码注入。兼容机制方案设计如图1所示。
图1 兼容机制方案设计
UA为一个特殊的字符串,在HTTP头中的user-agent字段中,用于服务器获取浏览器与操作系统的信息,如“Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv: 11.0) like Gecko”,Mozilla和like Gecko为习惯性字段,Windows NT 6.1为操作系统类型,Trident为IE浏览器内核,版本号为7.0。在向服务器请求网页时,根据网页地址伪装浏览器的UA,可以防止服务器拒绝访问。同时,浏览器无法直接识别ActiveX控件标签,需要在解析网页时注入JavaScript代码,将ActiveX控件标签改变为浏览器可识别的NPAPI插件标签,使浏览器加载AxPlugin。UA伪装和JavaScript代码注入在浏览器中解决了如何获取和识别ActiveX控件标签,小幅增加了浏览器的负担,但具有无需改动现有在线系统的优点。
在Linux操作系统中支持Windows应用程序几乎都离不开Wine。由于Windows操作系统的闭源性质,Wine开发者只能推测Windows API的内部实现,所以Wine没有完全兼容Windows应用程序,但目前PE格式加载、注册表和COM技术已经得到Wine的支持。ActiveX控件本质为COM组件,因此将ActiveX控件的运行环境由Wine负责[10]。既充分利用了Wine的支持能力,无需改动ActiveX控件,又避免了整体移植IE浏览器的复杂性。
消息机制基于Linux的管道通信技术,由AxPlugin和AxLoader负责。AxPlugin为NPAPI插件,负责启动Wine和AxLoader,并在浏览器与AxLoader间传输消息。AxLoader为exe可执行文件,负责转换消息并加载ActiveX控件,同时与AxPlugin传输消息。所以,AxLoader又需要具有Linux操作系统的IPC能力。Wine为在Linux操作系统中编译Windows应用程序提供了Winelib和WineGCC。Winelib含有Windows API的实现和Windows兼容头文件。WineGCC为GCC的封装,用于编译基于Winelib的C/C++应用程序。Winelib和WineGCC实现了一种Windows和Linux混合编程的技术,基于Winelib和WineGCC编译的Windows应用程序获得了访问Unix API的能力[11]。因此,AxLoader可以基于Winelib和WineGCC实现。
基于上述方案设计,浏览器根据网页地址伪装UA向服务器请求网页,并在解析网页时注入JavaScript代码,改变ActiveX控件标签,使浏览器加载AxPlugin。AxPlugin将启动Wine和AxLoader,负责在浏览器和AxLoader间传输消息。在Wine中,AxLoader加载GUID标识的ActiveX控件,负责在AxPlugin与ActiveX控件间转换和传输消息。
UA伪装和JavaScript代码注入需要浏览器的支持,本文以MiniBrowser浏览器作为实验浏览器。MiniBrowser浏览器为在WebKitGTK+上实现的简单浏览器,WebKitGTK+为WebKit内核的GTK+移植(port)。
UA伪装需要记录网页地址与UA的映射关系,因此采取了白名单机制。白名单格式为JSON,每条记录含有两个值,分别为网页地址与UA。白名单中的网页地址的形式如“https://*.demo.com/*”,支持通配符。白名单中的UA为不同版本IE浏览器的UA。在WebKit内核中,WebView为网页视图,站在用户的角度,WebView相当于浏览器中的标签(tab)。在WebView向服务器请求网页前,若用户访问的网页地址与白名单中的网页地址匹配,则获取WebView的WebKitSettings,将其UA设置为白名单中相应的UA,从而实现UA伪装,流程如下:
WebKitSettings *settings = webkit_web_view_get_settings(web_view);
if (address matched)
//地址匹配,伪装UA
webkit_settings_set_user_agent(settings, user_agent in whitelist);
else
//地址不匹配,还原为默认UA
webkit_settings_set_user_agent(settings, default user_agent);
webkit_web_view_load_uri(web_view, address);
//向服务器请求网页
WebKit内核具有JavaScript代码注入接口,主要为浏览器扩展提供支持。注意,扩展(extension)与插件(plugin)不同,扩展为HTML与JavaScript代码的集合,它可以包含插件[12]。在WebKitUserContentManager中设置WebKitUserScript,并在WebView初始化时传入,WebKitUserScript含有的JavaScript代码将在网页解析时执行,从而实现JavaScript代码注入。注入的JavaScript代码如下:
function listener(event) {
//事件监听函数
var node = event.target;
if (node.nodeName == ″OBJECT″) {
//若为
set type to ″application/x-axplugin″
set clsid to GUID
remove classid
remove codebase
}
}
window.addEventListener(″beforeload″, listener, true);
//为beforeload事件设置监听函数
上述代码利用WebKit内核支持的beforeload事件来改变ActiveX控件标签,使浏览器加载AxPlugin。网页中每一个标签加载前将触发beforeload事件。在beforeload事件监听函数中,向ActiveX控件标签添加type,其值为AxPlugin的MIME,设置为“application/x-axplugin”。classid在非IE浏览器中具有其他含义,因此将classid转换为clsid,移除codebase。JavaScript代码注入仍然需要白名单机制,防止JavaScript代码注入影响其他网页或者带来不必要的性能开销。
消息传输分为数据传输和函数调用转发两个方面的工作。AxPlugin加载后初始化管道并创建子进程,子进程将调用execvp转而执行AxLoader。在消息机制中,实现了Read和Write两组函数来负责AxPlugin和AxLoader间的数据传输,分别用于从管道中读出或写入C++数据类型以及NPAPI数据类型。如字符串“Hello”,编码后向管道中写入的字节流如图2所示。
图2 “Hello”字符串编码后的字节流
第一个字节代表命令类型,由Command枚举值定义,图中为Command::PushString的数值。其后三个字节为传输数据的长度,最大值为0xFFFFFF,图中为“Hello”(含‘ ’)的长度;其后的字节即传输的数据。NPAPI数据可以转换为C++数据类型。AxPlugin和AxLoader按照相同的规则在管道中读写数据,从而实现数据传输。
函数调用转发的形式与C/C++函数调用相似,在C/C++函数调用中,先将全部函数参数入栈,执行call指令跳转至函数代码块,接着从栈中读出函数参数,执行代码,最后将函数调用结果存入寄存器,执行ret指令。因此,Command枚举值除数据传输命令外,Command::Call和Command::Return为函数调用命令,并且由Message枚举值标识需要调用的函数,同时由Stack负责暂存数据。浏览器调用NPP函数和NPClass函数后,由AxPlugin转发至AxLoader。AxLoader调用NPN函数后转发给AxPlugin,由AxPlugin调用浏览器提供的NPN函数。如NPHasMethod函数调用,由AxPlugin转发至AxLoader的流程如下:
在AxPlugin中:
bool NPHasMethod(NPObject *obj, NPIdentifier name) {
//从右至左写入函数参数
WriteIdentifier(name);
WriteObject(obj);
Call(Message::NPHasMethod);
//发出Cammand::Call
Stack stack;
ReadCommand(stack);
//阻塞直到接收到Command::Return
bool result = stack.ReadBool();
return result;
}
在AxLoader中:
Stack stack;
ReadCommand(stack);
//阻塞直到接收到
Command::Call switch (stack.ReadMessage()) {
…
case Message:NPHasMethod: {
//读出函数参数
NPObject *obj = stack.ReadObject();
NPIdentifier name = stack.ReadIdentifier();
bool result = obj->_class->hasMethod(obj, name);
//进行真正的函数调用
WriteBool(result);
Return();
//发出Command::Return
}
}
在AxPlugin的NPHasMethod函数中,将函数参数从右至左写入管道中,并以Command::Call结尾。ReadCommand函数内部为一个循环,若从管道中读出Command::Call,则将Message枚举值存入Stack中,终止循环。若从管道中读出Command::Return,则直接终止循环。若从管道中读出数据传输命令,则将数据存入Stack中,继续循环。因此,AxLoader的ReadCommand函数将全部函数参数存入Stack中,直到接收到Command::Call。紧接着,switch语句根据Message枚举值执行恰当的代码块,从Stack中读出函数参数,调用相应函数。最后,AxLoader将函数调用结果写入管道,并以Command::Return结尾。AxPlugin在发送Command::Call后,其后的ReadCommand函数将函数调用结果存入Stack中,直到接收到Command::Return。最后,AxPlugin将Stack中的函数调用结果返回给浏览器。
消息转换由AxLoader负责,但本文没有在AxLoader中直接实现消息转换,存在前人的工作可以复用。Mozilla基金会的Adam Lock最早开始了相关方面的工作,项目名称为Mozilla ActiveX Control。其后,开源项目ffactivex借鉴了Adam Lock的工作开发了ActiveX控件的NPAPI封装,在内部加载ActiveX控件并实现消息转换。
ffactivex的工作分为两个方面:NPVariant类型到VARIANT类型的转换,NPClass函数调用到COM函数调用的转换。VARIANT为COM技术定义的变体类型。变体类型一般由标识类型的枚举值和存储值的union结构体组成,可以根据枚举值标识的类型来映射NPVariant与VARIANT的值,从而实现NPVariant类型到VARIANT类型的转换。无论NPAPI或者COM技术,都面向对未知接口的调用,因此NPClass函数与COM技术的一些函数十分相似。在转换数据类型后,NPClass函数可以调用相应的COM函数或者COM函数的组合来实现等价的逻辑,从而实现NPClass函数调用到COM函数调用的转换。仍然以NPHasMethod函数为示例,其转换到COM函数调用的流程如下:
bool NPHasMethod(NPIdentifier name) {
convert NPIdentifier to LPOLESTR
//将函数名转换为LPOLESTR类型
DISPID did;
IDispatchPtr disp = control.GetInterfacePtr();
//获取ActiveX控件的IDispatch接口
disp->GetIDsOfNames(IDD_NULL, &name, 1, 0, &did);
//查询函数名
return (did != -1);
}
将NPIdentifier类型的函数名转换为LPOLESTR类型,接着获取ActiveX控件的IDispatch接口指针。IDispatch接口含有的GetIDsOfNames函数将根据函数名查询其DISPID,判断did的值即可得知ActiveX控件是否含有该函数。NPHasMethod函数调用GetIDsOfNames函数实现了等价的逻辑。
ffactivex编译后为dll文件,但AxLoader在Wine中可以加载ffactivex.dll。AxLoader在接收到AxPlugin转发的函数调用时,真正的函数调用实际上由ffactivex.dll完成。同时,ffactivex.dll调用AxLoader提供的NPN函数,而AxLoader仅将NPN函数调用转发给AxPlugin,再由AxPlugin调用浏览器提供的NPN函数。
实现了一个电子时钟控件作为ActiveX测试控件,在 Windows和Linux中的效果分别如图3所示。
图3 测试控件在Windows与Linux中的实现效果
测试控件基于MFC ActiveX Control开发框架,由Visual Studio 2012编译,因此需要在Wine中配置VC++2012运行时库。在OnDraw函数中,测试控件调用图形设备接口GDI(Graphics Device Interface)的Polyon函数绘制图形构成数字。基于兼容机制,测试控件在Linux操作系统中正确显示时间,证明了其有效性。
针对ActiveX控件的加载时间和运行效率、内存占用三个方面做了评估。评估的硬件环境主要配置为Intel Core i7-2600处理器,8 GB内存,Seagate 1 TB机械硬盘,软件环境分为三种环境作为对比,分别为① Windows物理机:Windows 7操作系统,IE浏览器11.0。② Windows虚拟机:虚拟机软件为VirtualBox 5.1.10,虚拟机环境为①,宿主机环境为Ubuntu 16.04操作系统。③ Linux:Ubuntu 16.04操作系统,MiniBrowser浏览器,WebKitGTK+ 2.8.10,Wine 1.9.20。
(1) 加载时间
编写一组网页并嵌入数量不等的测试控件,利用JavaScript的onload事件获取控件加载完毕的时间。网页为本地文件,既方便实验,又排除了网络因素的干扰。在实验中,发现WebKit内核的JavaScriptCore引擎优化了JavaScript的执行顺序,在触发onload事件时,若代码中未调用控件的JavaScript可编程对象,控件将在onload事件后加载,导致获取的数据不正确。因此,在触发onload事件时调用了每一个控件的JavaScript可编程对象,以确保在记录数据时所有控件均加载完毕。IE浏览器中未发现此现象,但为了公平性,仍做相同处理。实验结果如图4所示。
图4 不同环境中测试控件的加载时间
测试控件在Windows操作系统中的加载时间非常短,IE浏览器加载50个控件的时间也不大于100毫秒,在Windows虚拟机中略大于在Windows物理机中。基于兼容机制,在Linux操作系统中测试控件的加载时间明显增加,其原因为JavaScript代码注入、启动Wine等均增加了额外时间。注意到,测试控件在网页中的数量与加载时间不成正比,因为在首个实例加载后,测试控件已加载至内存,浏览器只需向其申请实例。虽然兼容机制明显增加了加载时间,但与切换操作系统或者开启虚拟机的代价相比,几百毫秒的额外加载时间可以接受。
(2) 运行效率
实现了一个蒙特卡洛方法计算圆周率的ActiveX控件,它以循环的方式大量调用C 标准库的rand函数。实验结果如图5所示。
图5 不同环境中测试控件的运行时间
测试控件在Windows虚拟机中的运行时间明显大于在Windows物理机中。基于兼容机制,在循环次数较小时,测试控件在Linux操作系统中的运行时间大于在Windows物理机中,但在循环次数较大时,其运行时间反而小于在Windows物理机中。经进一步实验与分析后,原因为Wine的rand函数性能较Windows操作系统的rand函数性能优秀,在循环次数较大时,rand函数消耗的时间占比大,消息传输与转换的额外时间没有体现。借助Wine的优秀支持,测试控件在Linux操作系统中的运行效率媲美在Windows操作系统中。
(3) 内存占用
ActiveX控件为COM组件,加载后在内存中属于共享内存,测量其占用的内存空间较困难,且意义不大。仅测量了兼容机制所占用的额外内存空间,其中,wineserver和AxLoader共占用了约15.6 MB内存,内存占用代价可以接受。
ActiveX控件需要用户手动安装至Wine中,并配置相关的库。对ActiveX控件的支持程度取决于Wine,但一般来说,ActiveX控件仅完成轻量级的工作。除在网页中嵌入
本文提出的兼容机制充分利用了Wine对ActiveX控件的支持,采取中间件的解决方案实现了ActiveX控件运行在Linux操作系统的浏览器中,避免了整体移植IE浏览器带来的复杂性。对一般用户来说,兼容机制为用户访问含有ActiveX控件的网站提供了方便。对网站维护者来说,兼容机制无需改动现有的ActiveX控件与在线系统,节约了重复开发的成本。本文为ActiveX控件跨平台技术提供了一种新的方法。
[1] 陆峰.浏览器:25年入口之争[J].互联网经济,2016(3):90-97.
[2] 李爱国,高沙,吴韵格.基于ActiveX控件与Office对象模型的技术文档管理系统[J].计算机应用与软件,2014,31(12):148-151.
[3] 黄聪会,陈靖,罗樵,等.面向二进制移植的虚拟化技术[J].计算机应用研究,2012,29(11):4185-4188.
[4] Singh E G,Bhathal E G S.An overview of virtualization[J].International Journal of Computer & Technology,2013,5(3):167-171.
[5] 龚亚东,张辉,叶勇.WINE内核及实现MicrosoftWindow消息机理分析[J].计算机应用,2005,25(b12):431-433.
[6] 云吉,杨铸,姚严峰.视频监控系统跨浏览器插件的研究与实现[J].电子设计工程,2015(16):65-67.
[7] Fukai Y,Hirano Y,Itoh Y,et al.Web browser based GUI for TV[C]//The 1st IEEE Global Conference on Consumer Electronics,2012:579-580.
[8] Mozilla Foundation.Gecko Plugin API Reference[EB/OL].[2016-12-15].https://developer.mozilla.org/en-US/docs/Plugins/Guide.
[9] 潘爱民.COM原理及应用[M].北京:清华大学出版社,1999:376-414.
[10] 石磊,杨维康,杨孟辉,等.基于Linux的ActiveX控件运行机制的设计与实现[J].计算机科学,2007,34(12):268-272.
[11] Dale W,Gouget F,Hentschel A,et al.Winelib Introduction[EB/OL].[2016-12-15].https://www.winehq.org/docs/winelib-guide/winelib-introduction.
[12] 张令臣,王雷,向继,等.基于XPCOM代理的浏览器扩展行为分析技术研究[J].信息网络安全,2012(8):223-225.
DESIGNANDIMPLEMENTATIONOFBROWSERCOMPATIBLEACTIVEXCONTROLMECHANISMINLINUX
Feng Bowen1,2Ji Ruyi1,2Yu Jiageng2
1(UniversityofChineseAcademyofSciences,Beijing100190,China)2(LaboratoryforInternetSoftwareTechnologies,InstituteofSoftware,ChineseAcademyofSciences,Beijing100190,China)
Nowadays web browser has become an integral part of computer system. However, the world’s highest market share of Internet Explorer browsers, its widely used ActiveX control, cannot run in the Linux’s browser. Therefore, two browser plug-in frameworks are studied, NPAPI and ActiveX, and a compatibility mechanism is proposed. This mechanism is a middleware solution of which one end is NPAPI and the other end is wrapped ActiveX control in Wine. It implements message passing and conversion between NPAPI and ActiveX control. Experimental results show that based on this mechanism ActiveX control is able to run in Linux and its load time, effectiveness and memory usage are acceptable. We provide a new method for ActiveX control cross-platform technology.
ActiveX control Cross-platform Linux NPAPI WebKit
TP3
A
10.3969/j.issn.1000-386x.2017.10.003
2016-12-30。国家自然科学基金青年科学基金项目(61402451)。冯博文,硕士,主研领域:游览器兼容与安全。汲如意,硕士。于佳耕,高工。