图一
FrameLoader、DocumentLoader、DocLoader类结构
图二
主要Document类结构
图三
FrameView类主要结构
图四
总的说来,WebCore包含了浏览器引擎的核心部分如处理html、dom、css、svg、获取资源、渲染页面过程控制、回调/通知外壳程序以及与Javascript实现的Binding等等;
二、一个Http请求在WebCore中的主要流程
1、当调用webkit_web_view_open(url)时会触发core(webView)->mainFrame()->loader()->load(uri)(即调用FrameLoader.load)来发起一个Http页面请求;
FrameLoader.load方法的主要处理过程如图:
图五
2、一旦发起ResourceHandle::start,就会由网络库向web服务器发起一个http请求;
3、而MainResourceLoader作为一个ResouceHandleClient,提供了诸如didReceiveData()、didReceiveResponse()等回调接口以供网络库调用,一旦从web服务器获得相关数据后网络库部分则会调用相关接口如didReceiveData等;
4、MainResouceLoader::didReceiveData的主要回调处理过程如下图:
图六
5、通过回调didReceiveData()方法,进而调用Node.attach()方法,这样就会解析生成document,同时会创建frameview、domwindow等;
6、创建的frameview会触发layoutTimerFired时间Timer,进而调用layout()方法,从而触发RenderObject的创建、布局等,同时或许会invalidateRect,进而触发操作系统图形库的paint消息事件;
7、由程序主消息处理循环接收paint消息事件,进而获取对应frame,获取或创建GraphicContext,然后调用frame->view()->paint(&ctx,...),从而触发对应RenderObject树进行重画处理,这样一个完整的页面就会逐步的显示出来。
三、网络库、图形库、Javascript实现与WebCore的集成
为方便扩展及模块化,WebCore在处理浏览页面的过程中,往往使用了类似java或gecko中接口的概念,一般先定义一组公共接口或基类,然后由不同模块来实现。
如网络处理部分由WebCore提供一个ResourceHandle类,而在不同的目录如cf、curl、qt、soup、win等中在不同网络库的支持下对ResourceHandle类提供不同的实现,待编译时择机选择对应目录下的实现,这种方式从架构的角度看比较简单,但往往不能让程序同时使用多个网络库,进而由程序动态切换使用不同网络库实现,而gecko在xpcom的基础上提供了对于这种扩展形式的支持;其中Chrome对ResouceHanle类的实现基于WinHttp网络库。
同样WebCore对图形库的集成,也是采取这种方式来实现,如由WebCore提供一个GraphicsContext类,然后在不同的目录如cairo、cg、qt、win、wx中在不同的图形库支持下对GraphicsContext提供不同的实现。其中Chrome对GraphicsContext类的实现基于Skia图形库。
WebCore中实现的dom、html、svg、css等,往往需要通过一定的方式输出给Javascript的实现如JavascriptCore、V8,以便JS Engineer能认识这些dom元素等,并且能调用其中的方法,这种方式叫做Binding,为了便于将WebCore中相对固定的dom、html、svg、css接口等极其方便的Binding出去,WebKit使用了极其高效及神奇的方式来实现。
首先定义一组非标准的idl接口,然后通过运行一组perl脚本如generate-bindings.pl、CodeGenerator.pm、CodeGeneratorJS.pm等,就可根据idl接口定义,生成一组符合指定Javascript实现规则的脚本对象类。这样极大的减轻了开发人员的投入及编码错误的发生。
这一点与gecko中将不同的xpcom接口Binding给Javascript实现有本质上的差别,在gecko中通过xpconnect及一组classinfo来维护原生元素与JS对象之间的关系,不同原生元素对应的JS对象的创建及属性方法的Binding完全依赖于xpconnect的实现及classinfo的定义,要添加删除修改Binding的属性与方法,只需修改classinfo;而WebKit中Binding,相对简单明了,不同原生元素对应的JS对象的属性与方法由idl接口文件来定义,而具体实现则交给威力强大的generate-bindings.pl来对应生成实现的代码,这样编译时就可以轻松实现Binding。。。