嵌入式浏览器 Konqueror/embedded 的技术分析

1 Konqueror/embedded概述
目前,嵌入式浏览器已经逐渐成为高端手机和PDA的标准配置。已经面市的大多数嵌入式浏览器是商业版本的,像opera和MS explorer等,而Konqueror/embedded是符合GNU 条款的自由软件。Konqueror/embedded是针对嵌入式Linux,由著名的桌面操作环境KDE下的浏览器Konqueror派生出来的。Konqueror/embedded将Konqueror中关于KHTML、SSL、java script等内容继承了下来,同时简化了Konqueror中很多类的定义,剔除了依赖于KDElib部分,以适应在不同的嵌入式平台上移植和运行。两者都是基于Qt的,因此Konqueror/embedded也可以运行在Qt/X11环境下。Konqueror/embedded完整地支持HTML4和css1(部分支持css2)、java script(ECMAScript 262)、cookies、SSL、IPv6;支持和管理兼容XBEL的书签,并且能够很好地支持中文网页浏览;可以将Konqueror/embedded作为一个flashplayer、pppdialer或文件管理器使用。

2 Konqueror/embedded的构成
Konqueror/embedded的层次结构如图1所示。
嵌入式浏览器 Konqueror/embedded 的技术分析
Konqueror/embedded是由底层网路连接、图形化用户界面和处理HTML绘制的引擎——KHTML构成的。底层的通信协议实现是基于KIO/Slave机制来实现的;GUI界面采用了Kparts组件技术和Qt中的基本构件;而作为Konqueror/embedded的核心——KHTML,则运用了文档对象模型(DOM)所提供的API接口,并在DOM树上挂接java script引擎、CSS解析器以及渲染引擎。

3 Konqueror/embedded中的关键技术
3.1 底层通信协议的实现——I/O-Slave机制
在KDE中采用I/O-Slaves系统来访问各种数据。Konqueror/embedded沿用这种方式,通过I/O-Slaves和进程间管道通信机制实现完整的浏览器功能。在基于Qt/X11的Konqueror中,同样采用I/O-Slaves机制。简单说来,I/O-Slaves就是那些从网络上获取文件或目录的进程,它们依赖桌面通信协议DCOP(Desktop Communications Protocol)与其他进程进行通信;但是,DCOP的实现又依赖于X11 ICE(Inter Client Exchange)库。在嵌入式平台上移植体积庞大的X11 lib是不现实的。Konqueror/embedded采用了另外一种进程间的通信机制:通过管道(pipe)实现主进程和其他I/O-Slaves子进程之间的通信。
在KDE1.X之后,KDE的文件管理器和Konqueror等应用程序具有网络透明的特征,Konqueror/embedded也继承了这个特性。Konqueror/embedded不管对本地文件还是远程文件都采用URL(统一资源定位符)进行标识。网络透明性允许应用程序的用户使用与处理本地文件相同的方法来处理远程文件。在KDE的文件选择器能够通过诸如FTP、SMB甚至Webdav(在KDE3中)等方式列出远程目录。网络透明性和I/O-Slaves机制是靠KIO类来实现的。KIO类提供了几乎所有的文件管理功能。其中,KIO::NetAccess提供文件下载、上传以及临时文件的创建或删除等简单的同步访问功能。这是一种阻塞调用方式。如果用Konqueror/embedded下载网络上的数据,在数据没有完全加载之前,当前进程将会被阻塞。数据流完全加载之后,网页才可以再次渲染或刷新。
KIO::Job提供较为复杂的异步功能,包括打开、创建、复制、删除以及重命名等与文件或目录相关的操作。Konqueror/embedded正是依靠KIO::Job来实现网页访问这样的异步工作的。一旦某个job被启动之后,它将运行在后台,并且不会阻塞父进程。Konqueror/embedded中各种协议,如HTTP、FTP等是由一些独立的进程来实现的。这些独立的进程被称为Slaves。Slave是KIO::lavebase的子类,KIO::Slavebase中定义了一些虚函数,不同的Slave必须重载这些接口。Slave以函数库的形式存在于系统中,提供给相应的job调用。Slaveinterface运行在应用端(job),Slavebase运行在Slave端。一般情况下,创建的job会处在队列中,当事件循环处理到该job时,KIO为队列中的job分配适当的Slave。Slave的管理和调度是由调度器scheduler来执行的,scheduler将job队列加入适当的协议(Slave)中。当该job结束之后,Slave进程不会立即停止,会在空闲区域中等待一定的时间。这种机制的优点在于,如果有几个job访问的是同一个主机,那么就不需要重新启动新的进程,并且不需要再次进行协议握手。在同一时间,面向同一种网络协议,系统中最多只能启动3个Slave进程。如果引用该协议的job超过这个数目,那么多余的job将被添加到队列中,直到又有空闲的Slave进程可用。图2描述了Konqueror/embedded中关于底层协议(Slave)和任务(job)的实现过程。
嵌入式浏览器 Konqueror/embedded 的技术分析
3.2 GUI系统的实现——Kparts组件技术
(1)Konqueror/embedded中的组件
虽然,Konqueror/embedded的UI界面是直接继承QMainWindow的,但是,UI界面的功能和布局是由组件来实现的,QMainWindow只是这些组件的宿主。在Konqueror/embedded启动初始化过程中,首先加载一个特殊的组件——KHTM PART。KHTMLPART负责其他组件(如Kflashpart、Kplaintextpart等)的加载和管理。采用Kparts组件编程技术能够使得Konqueror/embedded的功能得到扩展,而不需要重新修改底层代码,同时增强了Konqueror/embedded的可定制性。Konqueror/embedded将Web浏览器、flsahpalyer、文本编辑器和简单音频播放器都作为Kparts组件嵌入到主窗口中。Kparts组件编程技术能够通过将图形组件嵌入应用程序的窗口使需要同一功能的应用程序共享一个组件。Kparts组件分只读组件和读写组件。只读组件ReadOnlyPart类为实现任何类型的查看器提供了一个公共框架。如果提供了一个文件的URL,那么所有这些查看器都可以显示该文件,并阻止对该文件的任何修改。在Konqueror/embedded中,像Web浏览器、flashpalyer、简单音频播放器都属于只读组件。另外一种读写组件ReadWritePart类是ReadOnlyPart的扩展,它添加了修改和保存文档的可能性,像Konqueror/embedded中嵌入的文本编辑器,属于读写组件。

(2)Konqueror/embedded中组件实现技术
以Konqueror/embedded中文本编辑器组件(Kplaintextpart)为例,组件必须要由三个元素组成,包括窗口构件、组件功能和用户界面。窗口构件必须是QWidget的子代,Konqueror/embedded 中的文本编辑器继承于QmultiLineEdit类;除了窗口构件外,还需要组件提供的功能,Konqueror/embedded的中文本编辑器提供前进、后退、剪切、复制和全选等附加功能;当然还要提供访问那些功能的用户界面(操作以及XML文件)。
Konqueror/embedded中的文本编辑器只提供了窗口构件功能的菜单项。在XML格式文件Kplaintextpart.rc中定义其用户界面的布局,它和应用程序代码是分开的。当该文本编辑器被嵌入到Konqueror/embedded时,采用称为XML-GUI的技术将组件菜单和原来的用户界面合并。Konqueror/embedded中的文本编辑器组件构成如图3所示。
嵌入式浏览器 Konqueror/embedded 的技术分析
Kparts组件的最大特征在于它的可重用性。要做到这一点,就必须将组件编译到共享库中。希望动态地打开共享库的应用程序使用KLibLoader类。KLibLoader处理库的定位、打开以及调用初始化函数。初始化函数是库的入口点。这个初始化函数创建一个工厂,由组件的工厂来创建组件。具体的实现方法如图4所示。
嵌入式浏览器 Konqueror/embedded 的技术分析
3.3 KHTML绘制引擎的实现
KHTML引擎作为Konqueror/embedded的核心,包含了dom、xml、html、css、rendering、misc、ecma七个子目录。这几个子目录的功能分别如图5所示。
嵌入式浏览器 Konqueror/embedded 的技术分析
从这个功能结构图可以看出,Konqueror/embedded的KHTML引擎是基于XML的DOM 技术来构建的,与现在的大部分浏览器的架构是相似的。DOM 是以层次结构组织的节点或信息片断的集合。在这种层次结构中可以利用导航仪搜寻特定的信息。同时,DOM 还提供了一套API,可以用JAVA,C 或C来实现这些API。
现在最常用的解析XML文件的方法有四种:文档对象模型(DOM,Document Object Model)、用于XML的简单API(SAX,Simple API for XML)、JDOM 和用于XML解析的Java API(JAXP,Java API for XML Parsing)。它们有各自的优点和弱点,因而适用在不同的场合。Konqueror/embedded中采用的是DOM 技术。它所提供的接口和方法可以对构建的DOM树的节点进行添加、删除,甚至可以删除树的几个部分,还可以重新排列树和添加新的分支;但是,由于DOM 构建整个文档驻留内存的树,如果文档很大,就会要求有极大的内存,这对于一般内存不大的嵌入式设备是个挑战。另外,DOM 构建整个文档的每个节点和元素,如果用户关心的只是其中的一部分,那么将会引起资源的浪费;同时,DOM是在用户获取控制权之前加载整个文档的,如果文档很大,将产生明显的延迟。Konqueror/embedded中,采用DOM 技术来解析HTML/XML的最大优点在于,DOM会自动地保存已经解析过的文档,而不必要在用户希望浏览历史的时候再重新解析文档。
Konqueror/embedded中采用的是DOM2级规范,分为DOM2 Core和DOM2 HTML。相对于DOM Level1,DOM2增加了对XML文档处理的一些接口和方法:

视图(view),提供视图与文档的联系; 样式表(style sheet),提供访问和修改样式表的方法; 层叠样式表(CSS2),提供CSS2兼容的方法; 事件(events),提供各种事件的接口; 文档遍历(document traversal),提供遍历文档层次的接口; 文档范围(document range),提供分割文档范围的接口。

4 在MC9328MX1上的应用
4.1 Konqueror/embedded的移植
笔者在自行设计的基于Motorola Dragonball MC9328MX1芯片的开发板上成功移植了Konqueror/embedded。该开发板上运行嵌入式Linux,并以Qt/embedded2.3.7作为图形引擎库,上层运行Qtopia1.6。
Konqueror/embedded是基于Qt的,并将依赖于Kde部分进行了简化和包含,因此,移植Konqueror/embedded到该开发板是完全可能的。
Konqueror/embedded提供了几十种编译选项,可以通过增减编译选项来定制适合不同平台,具有不同功能的Konqueror/embedded。Konqueror/embedded的非核心组件的实现是在add-ons目录下,例如如果需要播放Flash支持,必须在编译选项中加入--enable-add-ons=kflashpart。在该部件中,默认是将声音关闭的,可以通过修改代码将声音开关打开。
由于MC9328MX1芯片是基于ARM 架构的,所以移植的第一步是构建合适的交叉编译环境。笔者采用的是arm-linux-g 编译器。编译过程与一般的Linux软件编译的过程相似;但是,将Konqueror/embedded编译成功之后,单独下载Konqueror/embedded的可执行文件Konqueror到开发板上运行将会出现致命的错误。因为Konqueror/embedded的运行需要两个主要文件——htm14.css和charset,是分别关于样式表解析和字体设置的。

4.2 Konqueror/embedded的汉化
Konqueror/embedded的汉化可分为两个部分:一个是Konqueror/embedded本身控制界面的汉化;另一个是Konqueror/embedded对中文网站的访问支持。和其他基于Qt的应用程序一样,Konqueror/embedded中对所有用户可见的文本使用;对所有文字形式的文本使用tr()。tr()将文本标识出来,这样利用Qt提供的翻译工具将很容易把这些文本转化成所需要的语言;同时,Qt提供了两个宏:QT_TR_NOOP()和QT_TRANSLATE_NOOP()。用它们标示出文本,以便于被lupdate工具提取。具体操作步骤如下:

在需要翻译源码的XXX.pro中加入一项:TRANSLATIONS= XXX.ts。 运行lupdate XXX.pro,生成XXX.ts文件。该工具识别出tr()结构和上面描述的QT_*_NOOP宏,产生.ts文件(通常每种语言一个)。 运行lrelease XXX.pro将生成XXX.qm。这是一个没有翻译成其他语言的.qm文件(也可以用Qt Linguist生成),可以把它改名为XXX_en.qm。 启动Qt Linguist将XXX.ts文件导人,将需要翻译的内容翻译成中文。翻译完成之后,点击File→Release,将文件保存为XXX_zh.qm 翻译文件保存之后,在程序源码中需要构建Qtranslator实例,利用Qtranslator将翻译文件加载到图形界面上。

在Konqueror/embedded中可以通过修改main.c中的这段代码来实现UI的汉化:
QString qmFile="XXX_zh.qm";
QTranslator *translator=new QTranslator(&app);
if(translator->load(qmFile))
app.installTranslator(translator);
else
delete translator;
为了让Konqueror/embedded能够浏览中文网页,需要理解Qt对字符编码的处理。在Qt中采用Unicode编码的方式来存储、描述和运用字符串。让Konqueror/embedded支持中文页面浏览实际上与让Qt支持中文字体的概念是一样的。Qt/embedded/Qtopia中能够识别以下四种字体格式,并且必须是Unicode编码的:

TrueType(TTF)——Sealable PostScript Typel(PFA/PFB)——Scalable Bitmap Distribution Format fonts(BDF)—— non-Scalable Qt prendered Font(QPF)—— non-Scalable

其中.QPF格式是Qt为了减小字符集体积和减小内存消耗而定义的一种字符存储格式。在Qt/embedded/Qtopia中,采用这种格式的字符集。
在Qt/embedded中.提供转换QPF字体的工具——makeqpf。它是一个基于Qt/embedded的程序.编译之后运行在QVFB中。我们采用的是Qt/embedded2.3.7版本的makeqpf,运行时需要先启动QVFB,程序makeqpf会自动查找文件$QTDIR/lib/fonts/fontdir;所以.在此之前需要设置好QTDIR,告诉makeqpf所需要转换的字符集的各种属性。将MS Windows下的字符集simsun.ttc复制到$QTDIR/lib/fonts目录下,改名为simsun.ttf,在文件fontdir中添加如下的一行:
嵌入式浏览器 Konqueror/embedded 的技术分析
字符集转换完成之后,Konqueror/embedded中用来管理属性的preference类会在重启之后自动搜索系统可用字符类型,新的字体名称将会出现在选项栏中,以供选择。

5 总结
作为一款全功能的嵌入式浏览器,Konqueror/embedded运用了很多KDE程序设计的方法和思路。Kparts组件技术使得Konqueror/embedded具有良好的可扩展性,以适应不同用户或不同场合的应用;I/O-Slave机制让Konqueror/embedded能够通过各种网络协议透明地访问网络文件;而作为KHTML引擎的核心——DoM。使得Konqueror/embedded能够正确地解析和渲染HTML/XML文件。并在DOM结构树上绑定ECAMScript引擎和CSS解析器。可见在结构上,Konqueror/embedded和其他现代浏览器具有相似之处 Konqueror/embedded是基于Qt工具套件的.因此,Konqueror/embedded只能运行在以Qt/embedded为基础的嵌入式设备上或运行Qt/X11的PC机上.这在一定程度上限制了它的广泛应用。但是,由于它实现了完整的浏览器功能。并且是完全免费的,所以Konqueror/embedded仍然具有很强的吸引力.同时对于其他的嵌入式浏览器设计,它所包含的很多设计思想是值得研究和借鉴的。