出處:http://www.cnblogs.com/jyli/archive/2010/01/31/1660355.html
作者:李嘉昱
研究Webkit內核已經有一段時間了,在這期間我花了很多時間去研讀webkit代碼,并在網上查閱了不少的資料。在這之后,我開始嘗試對Webkit內核進行剪裁,以便得到自己所需的部分。
俗語有言,“獨學而無友 則孤陋而寡聞”,遂本著交流的態度拿出來與大家分享之,順便回顧一下自己這段時間以來走過的路。到目前,本人雖不能說是精 通Webkit內核 ,但也總算是在Webkit內核中游歷過了一番,并獲得了不少Webkit內核代碼的直接經驗。
在此,我將以Webkit內核代碼為基礎,詳細介紹Webkit內核的組成以及其各個模塊的功能和實現。文章寫到哪算哪,有了新的認識也會更新,補充。
什么是Webkit
Wekbit是一個開源的Web瀏覽器引擎,也就是瀏覽器的內核。Apple的Safari, Google的Chrome, Nokia S60平臺的默認瀏覽器,Apple手機的默認瀏覽器,Android手機的默認瀏覽器均采用的Webkit作為器瀏覽器內核。Webkit的采用程度由 此可見一斑,理所當然的成為了當今主流的三大瀏覽器內核之一。另外兩個分別是Gecko和Trident,大名鼎鼎的Firefox便是使用的Gecko 內核,而微軟的IE系列則使用的是Trident內核。雖然目前市場上IE內核瀏覽器仍然占據大多數市場份額,但其他內核瀏覽器也大有后來居上之勢。其 中,Webkit作為一款優秀的瀏覽器內核,它眾多優秀的特性引起業內的的廣泛關注。尤其是近來,google的加入更是讓Webkit有所升溫,從 Goole Chrome瀏覽器, Goole Anroid手機操作系統內置瀏覽器均采用Webkit作為內核, 近來推出的Chrome OS更是讓人期待。從實現角度來講,據說其比Gecko代碼更為整潔清晰,我目前還沒研究過Gecko的代碼,所以還無從比較。
Wekbit做了什么?
作為瀏覽器的內核,Webkit做了哪些工作?為了了解這些,先讓我們來看下一個Web瀏覽器究竟做了什么。我們可以從輸入輸出的角度來看一個 Web瀏覽器為我們做了哪些工作。先看一個簡單的例子,Web瀏覽器的輸入是一個HTML文檔,輸出則是一個我們用眼睛所看到的一個Web頁面, 就普通用戶而言它的輸入和輸出就是這么簡單,如下圖所示。
那么Webkit的輸入和輸出又是什么呢?如果能明白這個,那我們就能很清楚的知道Webkit到底是做什么的了。不過現在要說清楚這個還有點困 難,因為Webkit的輸出就要復雜些了,因為它的輸出本來就非直接面向用戶,現在簡單點來說,Webkit的輸入是web 文檔,輸出是一些看不見的模型,瀏覽器上層借助于這些模型來繪制出我們所看到的實際頁面。后面適當的時候會更具體的探討這些模型。
Wekbit組成
Webkit實際上包含三大部分,至少從代碼結構上來說是這樣的,當然,如果細分的話還能夠劃分出更多的模塊。如下圖所示
其中,WebCore是Webkit的核心部分,它實現了對文檔的模型化,包括了CSS, DOM, Render等的實現, JavaSript Core顯然是對JavaSript支持的實現。而橘黃色標注的Webkit部分包含了很多不同平臺對Webkit封裝的實現,即抽象出了與瀏覽器所能直 接對應的一些概念的實現,如,WebView,WebPage, WebFrame等。這三部分共同構成了Webkit, 在源碼中,它們分別對應這個這三個目錄, 即Webkit三大部分為WebCore, JavaSript Core,Webkit。
應用程序如何利用Webkit的
從下圖可以看出,利用Webkit的應用程序的位置處于Webkit模塊之上,應用程序并不直接和WebCore以及JavaScript Core來打交道,也不需要和它們打交道,而是同我前面提到過的Webkit模塊來交互,從而屏蔽了WebCore和JavaSript Core部分。Webkit模塊實際上抽象出了大多數應用程序所需要的那一部分,這里所說的大多數應用程序其實就是指的瀏覽器, 對于實現一個瀏覽器而言,Webkit模塊所提供的接口已經足夠。不過對于某些應用,可能得對其實施一定的改造,但是話又說回來,Webkit本來就是被設計成為Browser的內核而被實現的。
看到這里,也就是說如果我們要想利用Webkit來實現一個功能完善Browser或其他類瀏覽器應用程序的話,你只需要了解Webkit模塊部分 就足夠了,更確切的說只需要了解它所提供的接口就夠了。事實上,大多數平臺,如果采用了Webkit內核作為瀏覽器內核組件的話,所提供的對應文檔部分都 是關于Webkit模塊所提供的接口的描述。很多平臺都提供了Browser組件,其使用方法就像是其他可視組件一樣,在應用程序中應用起來非常方便。比 如,某平臺提供了叫做WebView的可視化組件,我們就可以通過如下幾行代碼來展示一個網頁, 使用的代碼僅僅三行。
WebView *view = new WebView();
view->load(Url("http://www.google.cn"));
view->show();
以Webkit作為瀏覽器組件的平臺有不少,包括Symbian S60, Android, Qt, GTK等等,前不久看到消息,好像Black Berry也準備在自家的手機上使用Webkit了。
CSS在Webkit中的實現屬于相對獨立的一個模塊,注意這里說的是相對。
CSS在Webkit中的作用自然是不言而喻的,在Web早期,文檔的結構和樣式還未分離的那個時代,HTML擔負了文檔的結構和樣式這兩個雙重任 務,即HTML既負責文檔的結構,同時文檔的樣式也通過HTML中通過標簽的屬性來指定??上攵?,在那個時候HMTL頁面的開發和使用比起現在而言是多 么的不便。
不過仔細想想,這恐怕與當時的技術發展程度有著很大大關,首先,那個時候互聯網遠不像現在這樣普及,另外,網頁也遠不如現在這樣復雜,不像現在, 可以說,世界上信息的主要傳播方式是以網頁形式出現的,沒數據說明,但我覺得至少趨勢是這樣的。 就這樣,互聯網在不斷的前進之中,直到后來CSS的出現,大大的改進了Web的開發模式,從此,文檔的結構和樣式被清晰的一分為二。HTML主要負責文檔 的結構,而CSS則擔負著文檔的樣式指定。
關于CSS的介紹網上已經有很多了,在這里將主要從Webkit實現的角度對其進行介紹。
CSS是什么
CSS是Cascading Style Sheets的縮寫,按照官方定義,它可以被認為是一個樣式表語言,它允許用戶通過它來為結構化文檔(HTML文檔)指定樣式。通過使用CSS用戶可以將文檔的內容和樣式分離,從而簡化Web頁面的開發和維護。
既然說它是一個樣式表語言,那么它就有相應的語法規則,規定了如何如何來書寫一個樣式表,讓其作用與文檔內容達到書寫者想要的外觀。CSS的語法規 則是比較簡單的, 自頂向下的來看,一個級聯樣式表(CSS)是由一系列的規則(rule)組成的, 每一條規則又是由一個選擇器(selector)和若干條聲明(Declearation)組成的。每條聲明(Declearation)又是一個鍵值 對,由屬性(property)和值(value)來組成,如下圖所示。
原圖片出處:http://dabrook.org/cc/Basic-Anatomy-of-a-CSS-Rule.png
從這里可以看到,語法是很簡單的,使用起來也確實很簡單。注意,我在這里只是說使用簡單,就跟鉛筆一樣,誰都會用,鉛筆的使用當然是簡單的不能再簡 單了,但是就是這樣普通的工具,在專業人士和普通人的手里所能創造出的東西是截然不同的。所以我想說的是,你能很快的學會如何使用CSS并不代表你編織出 漂亮的網頁,它只是一個工具,能發揮到什么程度還得看人。
轉回來,從其簡單的語法來看,似乎是只要簡單的將其轉化為對應的程序設計模型即可,但實際中,CSS的實現還是比較復雜的,其復雜性就在于,CSS 本身的復雜程度,它定義了一系列的規則來決定為哪些元素來指定樣式,以及樣式的繼承關系,哪些是繼承的,哪些是非繼承的,以及作用于同一個元素的多個樣式 的疊加,還有就是它對所有能指定的樣式都有完整的對應的屬性集。所以從實現的角度來看,一個完整的,兼容于標準的CSS實現,需要顧及到的東西還是很多 的。
CSS實現模型
webkit css部分的實現代碼為于目錄webcore/css中,算是webkit中一個相對獨立的模塊,下面類圖是我為了更好的了解css實現所作,大致勾勒出了CSS的內部實現。
用戶所書寫的css文檔,最終會轉化為webkit內部的模型表示,這里有幾個比較重要的類。
(待續...)
CSS默認樣式表
從Webkit CSS的實現可以看到,即使你不指定任何樣式表,實際上當CSS模塊運作起來的時候,它都會載入幾張默認的樣式表,要知道,在 CSSStyleSelector的構造函數中,總是會調用loadDefaultStyle()這個函數,其作用就是載入默認的樣式表。
這些默認的樣式表包含了一些HTML元素的最基本的樣式信息。相信在使用css的用戶中,大多數人都不會在對<div>指定樣式的時候 會為其添加一條display:block吧,是啊,幾乎所有使用css html的人都知道div是一個塊級元素,所以沒人會多此一舉,但是通過了解其CSS模塊的具體實現,我們可以知道,這些個默認的樣式表其實就已經為我們 指定了一系列我們認為的想當然的規則。
這四個默認樣式表是
額,從名字上大致也能夠了解1, 2了吧,它們不是以文件形式存儲,而是在CSS中以字符數組的形式出現,也就是說作為數據編到代碼里面去了,應該是考慮到每次都要使用默認樣式表而為了減少I/O造成的性能損失。
為了說明我前面所說的,這些默認樣式表描述的都是些個關于HTML元素的最基本的信息,還是來看個例子吧,
比如說html4UserAgentStyleSheet,從名字上可以看到,這張應該就是傳說中的瀏覽器默認樣式表了??纯炊加行┥栋?,這里只截取個片段。
1 html {
2 display: block
3 }
4
5 head {
6 display: none
7 }
8
9 meta {
10 display: none
11 }
12
13 title {
14 display: none
15 }
16
17 link {
18 display: none
19 }
20
21 style {
22 display: none
23 }
24
25 script {
26 display: none
27 }
28
29 body {
30 display: block;
31 margin: 8px
32 }
33
34 p {
35 display: block;
36 margin: 1.0__qem 0px
37 }
38
39 div {
40 display: block
41 }
42
43 layer {
44 display: block
45 }
從上面可以看出,真就是些最基本的屬性的指定,如果沒有這些默認值指定的話,用戶還得自行添加這些規則,那會很麻煩。
其他幾張表在此不作分析。
CSS解析
CSS使用的時候,只需要將按照其語法規范,書寫一個規則集合,然后保存為一個.css文件,在html中引用即可,當然這里使用的是外部樣式表的方式,只是使用CSS的一種方式,在這里我不打算討論CSS的幾種使用方式,所以都按外部的來。
那么這種按照語法規則書寫的CSS樣式表式如何轉換為Webkit內部的CSS模型的呢,這自然需要通過詞法語法分析。在這里,Webkit使用了 自動代碼生成工具生成了相應的代碼,也就是說詞法分析和語法分析這部分代碼是自動生成的,但它們不夠完整,然后我們需要自己寫一些配合性的代碼才能讓真個 CSS模塊工作起來,說的再白一些,就是需要我們自己是寫一些函數讓那些個自動生成的代碼來Call Back,用過其他各類解析器的朋友們應該很熟悉這個吧。如果誰對這部分代碼有興趣,可以研究一下。我倒是曾經為找一個跨平臺的bug調過這部分代碼,結 構還是蠻簡單的,代碼看起來稍多了些。入口是yylex和yyparse,有興趣可以自己看看。
那么Webkit中實現的這些個Call Back們在哪里呢?就在CSSParser中了,顯然,刨去生成的代碼不說,需要手工完成的CSS解析代碼部分就是這個了。CSS的一些解析功能的入口 也在此處,它們會調用lex,parse等生成代碼。相對的,生成代碼中需要的Call Back也需要在這里實現。
舉例來說,現在可以來看一個較大單位的回調函數的實現,createStyleRule(),該函數將在一般性的規則需要被建立的時候調用。
1 CSSRule* CSSParser::createStyleRule(CSSSelector* selector)
2 {
3 CSSStyleRule* rule = 0;
4 if (selector) {
5 rule = new CSSStyleRule(styleElement);
6 m_parsedStyleObjects.append(rule);
7 rule->setSelector(sinkFloatingSelector(selector));
8 rule->setDeclaration(new CSSMutableStyleDeclaration(rule, parsedProperties, numParsedProperties));
9 }
10 clearProperties();
11 return rule;
12 }
從該函數的實現可以很清楚的看到,解析器達到某條件需要創建一個CSSStyleRule的時候將調用該函數,該函數的功能是創建一個 CSSStyleRule,并將其添加已解析的樣式對象列表m_parsedStyleObjects中去,這里的對象就是指的Rule。那么如此一來, 經過這樣一番解析后,作為輸入的樣式表中的所有Style Rule將被轉化為Webkit 的內部模型對象CSSStyleRule對象,存儲在m_parsedStyleObjects中,它是一個Vector。
像這樣的函數還有createCharsetRule,createImportRule,createMediaRule等等,它們的作用大體上和createStyleRule類似,都是為創建Rule而準備的,只不過是不同類型的Rule。
了解了上面這些,大體上能夠就能夠了解CSS解析式怎么運作的。但是我們解析所要的結果是什么?通過調用CSSStyleSheet的 parseString函數,上CSS解析過程將啟動,解析完一遍后,所有的Rule都將存儲在對應的CSSStyleSheet對象中。但是這個時候的 規則依然是不易于處理的,需要將之轉換為CSSRuleSet,CSSRuleSet提供了一個addRulesFromSheet方法,能將 CSSStyleSheet中的rule轉換為CSSRuleSet中的rule,這樣所有的純樣式規則都會放存儲在對應的集合當中,這種集合的抽象就是 CSSRuleSet。以后就可以基于這些個CSSRuleSet來決定每個頁面中的元素的樣式了,后面會有介紹。
(...)
CSS如何作用于Render Tree
所謂的作用于Render Tree其實是指基于上面的解析成果來為相應的Render Object來指定特定的樣式,這個樣式的抽象就是RenderStyle(關于Render Tree可參見我的其他文章)。
(...)