XXX
分类: 嵌入式
2014-03-31 10:55:10
原文地址:开放源码的嵌入式浏览器概要设计 作者:自语的骆驼
开放源码的嵌入式浏览器概要设计
Copyright (C) 2001, Red Flag Software, Co. Ltd. All rights reserved.
该文档仅用于内部技术交流。未经许可不得转载全部和部分内容,
禁止随意散发, 严禁用于商业目的。
【文档名称】开放源码的嵌入式浏览器概要设计
【文档编号】SUM-DESIGN-BROWSER-V1.0
【编写人员】孙勇
【编写日期】2001/09/03
【文档内容】本文档进行将进行浏览器的概要设计,主要集中在系统框架的搭建上
【主要内容】
【修订记录】
序号 |
修订内容 |
修订人、日期 |
|
|
|
嵌入式浏览器实现的总体目标
在Konqueror以及Konq-Emb的基础上,实现以下的目标:
l 兼容HTML4.1
l 全面实现DOM Level1,部分实现DOM Level2与Level3
l 支持样式单,基本实现CSS1和部分实现CSS2
l 基本实现DOM与ECMAScript的绑定,支持JavaScript1.4
l 支持多字节语言以及双向排版
l 采用ViewML中采用的”Glue Layer”方法,实现渲染部分和GUI平台无关
l 支持HTTP1.1协议,支持非阻塞网络访问,通过OpenSSL支持SSL
l 支持Cookie
l 支持Netscape规范的Plugin规范
l 内建支持多种图象格式
嵌入式浏览器整体结构及其概要设计
我们简单的描述一下浏览器的工作过程:
1. 通过网络读取一个HTML文件
2. 交给XML/HTML的Parser进行解析,在解析的过程中如果发现新的URL(主要是图片、样式单以及Jscript外部代码),提交给网络部分去读取
3. 在解析的过程中按照DOM规范的要求形成一棵DOM树
4. 在形成DOM树的过程中,为每个DOM结点形成一个Render结点并且Render结点形成一棵Render树(Render树和DOM树的结构不一定相同)
5. 对Render树进行排版,最后进行输出。
6. 把解析过程中的JavaScript代码交给JavaScript解释引擎去解释执行。
接下来,我们对每个模块进行概要设计
Render部分的概要设计 设计思路与Render部分相连比较紧密的CSS部分,基本上是对CSS语法的分析,生成相应的属性和选择符,这部分与底层的GUI无关不需要大的改动,Kong-emb的Render类排版部分实现基本上也是与GUI无关,包括layout,calWidth,calHeight,calMinMaxWidth等等就不需要做什么大的改动。需要改动的地方主要是最后Render类的绘制,它是依赖与底层GUI的,下面,我们就简要说一下实现思路。
Qt模拟层Konq-emb的Render部分的实现是基于Qt的,因此需要把实现当中的Qt部分去掉。事实上Qt有着自己比较独特的signal-slot的机制,因此模拟siganl-slot机制肯定是我们的首要任务,我们可以访照Qt的实现,仍然采用Qt的moc预编译工具。
1. 实现一个QObject类,这个类主要包括connect,disconnect,activate_signale函数的实现。
2. 实现一个QMetaObject,这个类主要是维护signal/slot的资料,它应该是内聚在Qobject中的Qobject的函数的实现最终要依赖它来实现,存储了一些signal,slot的名字和它所对应的函数指针,这样就可以由QObject调用相应的槽函数。
Qt-widget的替换在Kong-emb的Render类里面基本上可以分为两类一种是和Qt-widget紧密相连的类,另外一部分是没什么大关系的,主要是利用QPaint和Qcolor进行一些图形的绘制工作,这一部分说作的工作并不是很大。另一部分和Qwidget相关的Render,这些类主要是RenderFormElement类的孩子类,这些类都有一个成员m_widget,m_widget是用Qt-widget实现的,并且做了一些相关的消息处理机制的设置,因此这部分我们要做的工作是用mini-gui的widget来代替它原来的widget实现。
QEvent的模拟在Render类里面,有一些函数是处理Qt-widget的消息的,这些消息是以QEvent传递的。对于这部分我们应该把它转换为对mini-gui的消息的处理。众所周之,消息的传递是通过一个消息循环来实现的,也就是说Qt的消息循环要转化为mini-gui的消息循环,这部分涉及到了整个浏览器的框架结构,整个浏览就是以这个消息循环为中心
Qt的数据收集类的模拟在Qt里面用到的一些模板和数据收集类可以自己重新实现,包括象QconnectionList等。
移植后的体系结构KHTMLPart的解决方法
KHTMLPart在Konqueror中有大量的关联,我们要完全去除KHTMLPart会有很大的工作量。KPart主要是为了基于组件开发的方式而设计的,Konqueror采用KHTMLPart进行HTML的渲染,KHTMLPart中可能还会嵌入别的Part对象(有可能就是KHTMLPart自己,在多Frame的情况下)。我们参考了Konq-Emb中Part的改造方法,应该说它基本上延续了KPart的构造,基本没有什么大的本质的变化(参见我们的分析文档)。
显然,我们不需要考虑组件嵌入等问题,KHTMLPart中的这一部分对我们来说没有用,特别是它KHTMLPartBrowserExtension和KHTMLPartHostBrowserExtension以及KHTMLPartBrowserInterface,我们有的需要去除有的需要精简;但是KHTMLPart作为浏览器的一个中枢接口的作用还是有它存在的必要的,它是KHTML这个HTML渲染引擎的入口和出口。
用户的输入通过窗口系统的事件机制最终输入到KHTMLPart中,由KHTMLPart进行处理;KHTMLPart中还包括最后在窗口系统中进行窗口/控件的渲染操作的部分,它有向窗口系统的输出,而窗口系统中的一些事件(特指焦点变化,窗口位置/大小变化等)需要输入到KHTMLPart进行处理;KHTMLPart要为有事件属性的DOM结点注册由JavaScript模块提供的事件监听者(比如),从而建立一个Event的通道,JS的解释执行就是要操作KHTMLPart中的DOM文档,可能引起重绘/更新的操作。KHTMLPart需要提交Job请求给IO模块,IO模块需要返回数据和一些状态信息(比如超时、授权错误、认证需求等等)。
KHTMPart需要我们在改造的实践过程中不断的尝试,才可以找到合适的方法。但是基本上,我们计划采用Doc/View结构对KHTMLPart进行抽象(他们有一定程度的相似)。
DOM模型实现
本文档中的DOM部分指:
1. DOM 1、DOM 2规范的实现
2. HTML/XML分词
3. 构建DOM树
4. JavaScript实现
DOM部分在浏览器中处于一个相对独立的地位,和平台的依赖性相对较少。根据我们分析的Konqueror和Konqueror-embedded浏览器,虽然这两种浏览器基于Qt和KDE,然而DOM部分与Qt和KDE依赖并不大。因此,浏览器DOM部分的设计主要是以移植Konqueror-embedded浏览器相应源码为主,而不是另起炉灶重新设计一套新的DOM实现方案。这样做除了减少开发工作量以外,还能带来以下好处:
1. 开发的浏览器能够随着Konqueror-embedded的升级而迅速升级;
2. 引入bug少
由以上分析,本文档主要侧重于在Konqueror-embedded浏览器DOM实现部分分析的基础上进行移植方案的设计。Konqueror-embedded关于DOM部分源码主要集中在khtml文件夹中,包括dom、html、xml、ecma、css、misc、等共六个子文件夹。分析如下:
1. dom:对应DOM规范中定义的对象,用C++语言定义了相应的类;此部分与平台相关性小,只引用了qstring.h和qrect.h等少数几个头文件;移植时应该考虑重新实现Qstring和Qrect等类。
2. html:这部分包括三部分内容:DOM HTML模块的具体实现;HTML文档的分词;DOM树的构建。由于在生成DOM树的过程中同时要生成render树;同时每个结点与一个khtmlview类型的对象相对应,因此,这部分源码引用了一些Qt控件类和render部分的类。这部分在移植第一阶段可以暂时屏蔽,即首先只考虑在其他平台(如MiniGUI)上生成DOM树,暂不考虑render树的生成。
a) Dtd.h/Dtd.cpp:对HTML/XML文档进行合法性检查,与平台无关,可直接移植;
b) Html_*Impl.h/Html_*Impl.cpp:DOM HTML模块定义的对象的实现,与Qt和KDE地相关性主要体现在两方面:引用了一些Qt和KDE的基础类实现DOM对象;引用Qt控件类实现与窗口和render相关的功能。
c) htmltokenizer.*/htmlparser.*:html/xml文档分词、构建DOM树。与render部分相关。
3. xml:这部分同html部分类似,只不过处理xml类型的文档,此处略去分析。
4. css:这部分包括两部分内容:DOM CSS模块的具体实现;CSS文档或字符串的分词;这部分与Qt和KDE相关性比较小,只用到了一些基础类,可考虑替换。
5. misc:一些常量的定义、装载stylesheet、image等作业的类以及译码类;
6. ecma:与js相关,与Qt和KDE的相关性没有经过详细分析。
除了上面几个子文件夹外,khtml文件夹中还包括rendering子文件夹,这部分涉及render树生成及排版实现部分,与Qt和KDE紧密相关,在移植第一阶段可暂不考虑。
经过以上分析,DOM部分的移植应该以分阶段渐进的方式进行:
第一阶段:实现一个与平台无关的具有有限功能的DOM模块:
1. 保持接口不变,替换一些Qt和KDE的基础类(与平台无关);
2. 预留和网络部分以及和render部分的接口;
3. 分词功能
4. 构建DOM树;
5. 暂时屏蔽有关js部分;
第一阶段达到效果如下:
第二阶段:根据总体设计,考虑先移植js部分,还是先和网络部分或render部分集成,目前待定。
JavaScript引擎以及同DOM规范的Binding
在Konqueror中,实现了符合ECMAScript规范的JS解释引擎(kjs目录下,由C++实现),并且实现了DOM规范同ECMAScript的绑定(khtml/ecma目录下)。我们还查阅了Mozilla使用的SpiderMonkey引擎的一些内容,这个引擎是用C写的,并且文档比较丰富。
在Konqueror中ECMAScript同DOM规范的绑定,同KJS解释引擎的内部定义相关联。比如KJS::DOMObject是从KJS::HostImpl中派生而来的,KJS::DomObject是DOM规范同ECMAScript绑定后对应的一个对象,而KJS::HostImpl是ECMAScript内部定义的一个class(用来说明ECMAScript规范中的Host对象)。除非重新定义DOM-ECMA绑定,否则我们在移植过程中必须使用KJS解释引擎。我们如果要使用SpiderMonkey,就需要重新定义DOM-ECMA绑定。
Konqueror中KJS部分提供了一个接口,一个叫做KJSProxy的类。访问KJS只需要通过这个KJSProxy进行。
网络访问模型
通过对Konqueror的分析,我们基本上了解了浏览器的IO模型和它的功能需求。Konqueror中的KIO模型是一个三层父子关系的进程模型(详细的情况请参见我们的分析文档),实际执行访问操作的是最下层的孙进程,三层进程间有很复杂的通讯。可能因为KIO是应用在整个KDE应用中的,为了整体结构的考虑设计了这样一个三层模型。但是对于浏览器的独立应用来说的话,显然太复杂和冗长了。
我们考虑采用两层的一个进程模型来完成网络访问,在子进程中进行多路I/O操作:
在主进程中的Cahche主要包括页面缓冲(只包括HTML的文本页面)、Image/CSS/Jscript缓冲、Cookie缓冲、授权信息缓冲。网络服务子进程中的缓冲主要就是SSL的认证信息缓冲。
我们准备使用LibWWW完成这个模型底层的网络访问工作(关于LibWWW的结构和使用请参看我们的分析文档)。同时要定义好上层进程和网络服务子进程之间的通讯形式和通讯协议(类似DCOP),以透明的API形式提供给上层应用。