WebKit的结构与解构

从指定一个HTML文本文件,到绘制出一幅布局复杂,字体多样,内含图片音频视频等等多媒体内容的网页,这是一个复杂的过程。在这个过程中Webkit所做的一切,都是围绕DOM Tree和RenderingTree这两个核心。上一章我们谈到这两棵树各自的功用,这一章,我们借一个简单的HTML文件,展示一下DOMTree和Rendering Tree的具体构成,同时解剖一下Webkit是如何构造这两棵树的。

新时代新潮流WebOS <wbr>【20】WebKit的结构与解构

Figure 1. From HTML to webpage, and the underlying DOM tree andrendering tree.
Courtesyhttp://farm4.static.flickr.com/3351/3556972420_23a30366c2_o.jpg

1. DOM Tree 与 Rendering Tree 的结构

Figure 1中左上是一个简单的HTML文本文件,右上是Webkit renderingengine绘制出来的页面。页面的内容包括一个标题,“AI”,一行正文,“Ape'sIntelligence”,以及一幅照片。整个页面分成前后两个层面,标题和正文绘制在前一个层面,照片处于后一个层面。L君和我亦步亦趋地跟踪了,从解析这个HTML文本文件,到生成DOMTree和Rendering Tree的整个流程,目的是为了了解DOM Tree和RenderingTree的具体成份,以及构造的各个步骤。

先说Figure 1中左下角的DOMTree。基本上HTML文本文件中每个tag,在webkit/webcore/html中都有一个class与之对应。譬如<HTML>tag 对应HTMLHtmlElement,<HEAD> tag对应HTMLHeadElement,<STYLE> tag对应HTMLStyleElement 等等。比较特别的是DOMTree的根节点,HTMLDocument,在HTML文本文件中没有哪个tag与之对应。关于HTMLDocument的作用,我们稍后介绍。整个DOM Tree的结构,与HTML文本文件中各个tags的嵌套关系也一一对应。一言以蔽之,DOMTree就是把HTML文本文件翻译成object树状结构。

需要强调的是,DOM Tree是一个通用数据结构,任何XML文本文件都可以翻译成DOMTree,而不仅仅限于HTML文本文件。webkit/webcore/html 中林林总总htmlclasses,基本上都是webkit/webcore/dom 中的某个class的子类,也就是说,/html 是/dom的一个特例。这样的设计,为将来把Webkit拓展到HTML格式以外的页面的布局和渲染,埋下了伏笔。所以严格地讲,Figure1中左下的DOM Tree,实际上是一个HTML DOM Tree。

再看Rendering Tree,显著的特点在于,

a. 整个Rendering Tree树状结构,与HTML DOM Tree树状结构一一对应。也就是说,几乎每个HTML DOMTree中的节点,在Rendering Tree中都有对应的节点。节点与节点之间的父子或兄弟关系也一一对应。

例外的是,在HTML DOM Tree有HTMLStyleElement叶子节点,而在RenderingTree中,没有相应的叶子节点。原因是,RenderingTree各个节点,都涉及页面中某块区域的布局和渲染。而HTMLStyleElement,并不直接涉及某块区域的布局和渲染,HTMLDOM Tree中HTMLStyleElement叶子节点包含的内容,已经融入RenderingTree中RenderImage叶子节点的属性中去了。另外,因为RenderingTree中不存在与HTMLStyleElement相应的叶子节点,所以,与HTMLHeadElement对应的节点也没有必要存在。

b. webkit/webcore/rendering中各个class与HTML tags并没有一一对应的关系。

RenderingTree是一个通用的规划页面布局和渲染的机制,这个通用机制可以服务于HTML页面,但是并不仅仅限于为HTML页面服务,我们可以用Rendering Tree来规划其它格式的页面的布局和渲染。以DOM Tree和RenderingTree为核心的Webkit渲染机,是一个功能强大,扩展性良好的通用渲染机。它不仅可以用来绘制HTML页面,也可以用来渲染其它格式的页面,譬如可以用它来制作email阅读和管理器,制作数据库管理工具,甚至制作游戏界面。

稍微让人有点吃惊的是,对于HTMLHtmlElement,HTMLBodyElement,HTMLHeadingElement和HTMLParagraphElement,在RenderingTree中通通以RenderBlock呼应。如果说HTMLHeadingElement和HTMLParagraphElement的区别不大,仅仅是字体和对齐方式有些微小的差别,所以RenderingTree可以用RenderBlock来统一应对。那么问题是,HTMLHtmlElement和HTMLBodyElement是两种容器,总是出现在DOM Tree的中部,而从来不会作为叶子节点出现,对应于这样的容器节点,为什么RenderingTree不另设一种class,与RenderBlock有所区别呢?不过话又说回来,这不是个大问题,最多是个美感的问题。


新时代新潮流WebOS <wbr>【20】WebKit的结构与解构
Figure 2. The construction sequence of the root of the DOMtree.
Courtesyhttp://farm4.static.flickr.com/3010/3554310018_e34d271344_o.jpg

2. DOM Tree 与 Rendering Tree 的根节点

前一节中我们提到HTMLDocument是一个比较特殊的class,它是整个HTML DOM Tree的根节点,但是不对应任何HTMLtag。JavaScript中经常出现的document,指的就是这个根。例如,
 
    “document.getElementByIdx(x).style.background="yellow";”

HTML文本文件,通常是以<HTML>开头,以</HTML>结尾。但是<HTML>tag并不对应DOM Tree的根节点,而是根以下的第一个子节点,即HTMLHtmlElement节点。

初看Figure 2 觉得有点意外,当用户在浏览器里打开一个空白页面的时候,立刻生成了DOMTree的根节点HTMLDocument,与RenderingTree的根节点RenderView。而这个时候,用户并没有给定URL,也就是说,对于浏览器来讲,这时候具体的HTML文本文件并不存在。根节点与具体HTML内容相脱节,或许暗示了Webkit的两个设计思路,

a. DOM Tree的根节点HTMLDocument,与RenderingTree的根节点RenderView,可以重复利用。

当用户在同一个浏览器页面中,先后打开两个不同的URLs,也就是两个不同的HTML文本文时,HTMLDocument和RenderView两个根节点并没有发生改变,改变的是HTMLHtmlElement以下的子树,以及对应的RenderingTree的子树。

为什么这样设计?原因是HTMLDocument和RenderView服从于浏览器页面的设置,譬如页面的大小和在整个屏幕中的位置等等。这些设置与页面中要显示什么的内容无关。同时HTMLDocument绑定HTMLTokenizer和HTMLParser,这两个构件也与某一个具体的HTML内容无关。

b. 同一个DOM Tree的根节点可以悬挂多个HTML子树,同一个RenderingTree的根节点可以悬挂多个RenderBlock子树。

在我们目前所见到的浏览器中,每一个页面通常只显示一个HTML文件。虽然一个HTML文件可以分割成多个frames,每个frame承载一个独立的HTML文件,但是从DOMTree结构来讲,HTMLDocument根节点以下,只有一个子节点,这个子节点是HTMLHtmlElement,它领衔某个HTML文本文件对应的子树。RenderingTree也一样,目前我们见到的网页中,一个RenderView根节点以下,也只有一个RenderBlock子节点。

但是Webkit的设计,却允许同一个根以下,悬挂多个HTML子树。虽然我们目前没有看到一个页面中,并存多个HTML文件,并存多个布局和渲染风格的情景,但是Webkit为将来的拓展留下了空间。前文中所设想的个性化,多皮肤,多视角的浏览器页面绘制,用Webkit实现起来难度不大。


新时代新潮流WebOS <wbr>【20】WebKit的结构与解构
Figure 3. The construction sequence of the DOM Tree and theRendering Tree.
Courtesyhttp://farm4.static.flickr.com/3627/3554182242_b0bec88534_b.jpg

 
3. DOM Tree 与 Rendering Tree 的构筑

HTMLDocument根节点包含的最重要的构件是HTMLTokenizer,而HTMLTokenizer又包含HTMLParser这个构件。HTMLTokenizer从前到后读取HTML文本文件中每一个字符,并从中提取出各个HTML tags以及它们的内容。而HTMLParser不仅负责HTMLDOM Tree的构筑,而且也同时负责Rendering Tree的构筑。

在Figure 3中,从第8步到第11步,HTMLParser根据一个HTML Tag生成一个HTML DOMTree节点。从第12步到第17步,生成相应的Rendering Tree的节点,并把它和HTML DOMTree的节点勾连在一起。这张图的细节过多,读解不容易。Figure 4把第8步到第17步演示了一下。


新时代新潮流WebOS <wbr>【20】WebKit的结构与解构


Figure 4. An illustration of the construction of a DOM tree nodeand its corresponding Rendering tree node.
Courtesyhttp://farm4.static.flickr.com/3306/3554259140_3deb9736ea_o.jpg

值得注意的是,每当HTMLParser生成一个DOM Tree的节点的时候,相应地,也同时生成一个RenderingTree节点。然后把它们两个新节点勾连在一起。换而言之,Rendering Tree与DOM Tree同步生长。

Webkit 值得赞赏的地方非常多,但是HTMLParser让DOM Tree和RenderingTree同步生长的做法,却值得商榷。如果同步生长,那么Rendering Tree必然平铺直叙地刻板地忠实于DOMTree。假设先生成DOM Tree,再生成RenderingTree,把两者割裂开,就有机会让Webkit发挥更加奇妙的布局和渲染。平铺直叙固然符合大多数人在大多数时间里的阅读习惯,但是离经叛道的设计,也会有市场。一个例子就是上一章末尾处那张多视点的地图。如果让DOMTree与Rendering Tree同步生长,这样的布局和渲染是难以想像的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值