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同步生長,這樣的佈局和渲染是難以想像的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章