轉(zhuǎn)載請(qǐng)注明原文地址:http://blog.csdn.net/milado_nju/article/details/7685517
# CSS在WebKit和Chromium中的實(shí)現(xiàn)
## 概述
前面章節(jié)介紹了CSS的三種基本要素,大概可以分成選擇器,各種基本樣式和CSS3引入的變形、變換和動(dòng)畫等。本章在此基礎(chǔ)上,著重介紹CSS是如何在WebKit和Chromium得到支持的。首先介紹的是CSS解析器,而后分別闡述上面三種基本要素如何在WebKit和Chromium中實(shí)現(xiàn)的。
接前面章節(jié),這里仍然以之前的CSS例子為基礎(chǔ)來介紹本章的內(nèi)容。為方便起見,依舊包含該例子,如下圖所示。
## CSS解析器
CSS的詞法其實(shí)并不復(fù)雜,其分析工作由bison完成,具體實(shí)現(xiàn)可以在文件CSSGrammar.y中找到,詳細(xì)過程這里不在贅述。其主要工作是解析詞法并調(diào)用CSSParser的回調(diào)函數(shù)來生成結(jié)果,例如上例中的第4行到第17行,第4行首先是識(shí)別選擇器,后面是各個(gè)屬性的識(shí)別,最后,這些全部構(gòu)成了基本的StyleRule類型(關(guān)于類型會(huì)在后面介紹),所以調(diào)用CSSParser的createStyleRule函數(shù),該函數(shù)將選擇器和屬性列表的中間表示最后處理生成其內(nèi)部表示保存在一個(gè)規(guī)則StyleRule中。對(duì)于其他的類型,作類似的處理。
解析器對(duì)上層的接口是CSSParser,所有任務(wù)均有其處理。那么上面例子中有哪些場(chǎng)景會(huì)需要?jiǎng)?chuàng)建CSSParser的實(shí)例呢?顯而易見地有兩處地方,第一個(gè)是第4行到第17行,當(dāng)DOM建立好之后在創(chuàng)建RenderObject的時(shí)候會(huì)調(diào)用CSSParser;第二個(gè)是第29行,在該段JavaScript代碼被執(zhí)行時(shí),JavaScript引擎會(huì)間接調(diào)用CSSParser為元素的屬性style解析。但其實(shí)還有一個(gè)地方會(huì)使用到CSSParser。
實(shí)際上WebKit(其他渲染引擎應(yīng)該也類似吧K)會(huì)為每個(gè)網(wǎng)頁設(shè)置一個(gè)缺省的樣式,這決定了你所沒有設(shè)置的元素及其它們的屬性缺省值和將要顯示的效果。通常來講,不同的WebKit的移植(port)會(huì)有不同的缺省樣式。
解析后的結(jié)果應(yīng)該是一系列的樣式規(guī)則,下面讓我們來看看這些規(guī)則在WebKit中的內(nèi)部表示。
## 樣式的內(nèi)部表示
被解析后的CSS樣式其實(shí)就是一組樣式規(guī)則,每一個(gè)規(guī)則包含一組選擇器和一組樣式屬性。如前面例子中第4行到第17行所示。這些數(shù)據(jù)在WebKit中都有相應(yīng)的內(nèi)部表示,為了便于理解,下圖給出了CSS的表示和內(nèi)部結(jié)果表示的對(duì)應(yīng)圖。
值得一提的是選擇器部分,上圖是一個(gè)選擇器列表,這在現(xiàn)實(shí)中是比較常見的。舉個(gè)例子, a[class=abc]其實(shí)是兩個(gè)選擇器,第一個(gè)是’a’,它一個(gè)標(biāo)簽選擇器;第二個(gè)是’[class=abc]’,它是一個(gè)屬性選擇器。
這里例子中的樣式規(guī)則是一種基本的樣式類型。CSS的標(biāo)準(zhǔn)包含了多種規(guī)則類型:
Style:這個(gè)是基本類型,一般大多數(shù)規(guī)則屬于這個(gè)類型;
Import:是WebKit中為方便引入的,其對(duì)應(yīng)的是一個(gè)導(dǎo)入CSS文件的Style元素
Media: 對(duì)應(yīng)于CSS標(biāo)準(zhǔn)中的@media類型
Fontface: CSS3新引入的自定義字體的規(guī)則類型
Page: 對(duì)于CSS標(biāo)準(zhǔn)中的@page類型
Keyframes: WebKit的@-webkit-key-frames類型,可以用來指定特定幀的樣式屬性信息
Region:對(duì)CSS標(biāo)準(zhǔn)正在進(jìn)行中的Regions的支持,這方便了開發(fā)者對(duì)頁面進(jìn)行分區(qū)域來排版。詳見參考文獻(xiàn)2.
下圖給出了這些類型在WebKit中的定義及其關(guān)系。
這里有必要解釋一下StyleRuleImport類,這個(gè)一個(gè)偽類型,CSS中并沒有該類型的定義,只是WebKit處理CSS文件方便引入的。該類需要兩個(gè)類來輔助完成,一個(gè)是StyleSheetContents類,就是CSS文件中各個(gè)規(guī)則的處理和內(nèi)部表示,這些規(guī)則被稱為子規(guī)則,構(gòu)成一個(gè)列表;另外一個(gè)是CSSStyleSheet,該類是個(gè)包裝類,代表CSS文件,包含一個(gè)StyleSheetContents對(duì)象。
## 選擇器(CSSSelector類)
CSS選擇器的實(shí)現(xiàn)并不復(fù)雜,其實(shí)現(xiàn)由類CSSSelector來完成。CSSSelector的作用是儲(chǔ)存從解析器生成的結(jié)果信息以被匹配使用。這里匹配指的是當(dāng)需要為每個(gè)DOM中的節(jié)點(diǎn)計(jì)算樣式適合,WebKit需要根據(jù)當(dāng)前的節(jié)點(diǎn)信息來從規(guī)則列表中找到能夠符合調(diào)節(jié)的規(guī)則,并把規(guī)則中的屬性列表提取出來生成節(jié)點(diǎn)的樣式信息。
在CSS介紹那一章中,我們知道有多達(dá)42種選擇器類型,例如。在WebKit的CSSSelector類中,它們被稱為偽類型(PseudoType),其含義是表示CSS標(biāo)準(zhǔn)中定義的類型,而主要用于匹配算法的類型定義為Match,它包含Id, Class, Extract, Set, List, Hyphen,PseudoClass,PseudoElement, Contain, Begin, End, PagePseudoClass。這些類型是通過抽象標(biāo)準(zhǔn)中的類型而得來,例如:first-of-type,:last-of-type,:only-of-typed等等都屬于PseudoClass。具體的匹配算法參考代碼(文件CSSSelector.cpp),這里不在贅述。
### StyleResolver和RenderStyle
在樣式規(guī)則解析完成之后,剩下的問題是如何把這樣規(guī)則應(yīng)用到具體的元素上。這就涉及到兩個(gè)主要的類:StyleResolver和RenderStyle。
StyleResolver是管理類,其負(fù)責(zé)根據(jù)樣式規(guī)則為每一個(gè)Document中的元素匹配響應(yīng)的樣式屬性,它和Document 節(jié)點(diǎn)是一一對(duì)應(yīng)關(guān)系,也就是說WebKit為每個(gè)Document創(chuàng)建一個(gè)StyleResolver對(duì)象,為所有該Document中的節(jié)點(diǎn)計(jì)算樣式,并將其結(jié)果保存到RenderStyle對(duì)象中。
RenderStyle是元素所有樣式屬性的內(nèi)部表示。由于其包含了所有樣式屬性,為了節(jié)約空間,WebKit將屬性分為兩種類型:常用屬性和非常用屬性。非常用屬性會(huì)進(jìn)行分組合并,并且僅在需要時(shí)創(chuàng)建,這相對(duì)有效地節(jié)約了內(nèi)存。該對(duì)象在被StyleResolver創(chuàng)建后由該元素所對(duì)應(yīng)的RenderObject所擁有。
那么StyleResolver是如何為一個(gè)DOM元素生成RenderStyle對(duì)象的呢?大致地有如下幾個(gè)主要步驟:
1. 首先創(chuàng)建一個(gè)新的RenderStyle對(duì)象
2. 從它的父親那里繼承它的一切可以繼承的屬性
3. 如果是link類元素,設(shè)置link屬性
4. 而后是樣式規(guī)則的匹配,從已知規(guī)則中找到匹配到的屬性
5. 將匹配到的屬性應(yīng)用到RenderStyle對(duì)象中
6. 為該DOM元素的RenderStyle做一些修正工作
7. 清理StyleResolver,為下次匹配請(qǐng)求做準(zhǔn)備
## RenderStyle 和RenderObject
元素在匹配生成其樣式屬性值之后,RenderStyle對(duì)象被RenderObject所獲得,這個(gè)觸發(fā)一個(gè)重新繪制的動(dòng)作,WebKit此時(shí)可以根據(jù)樣式屬性值來計(jì)算它的布局和顯示,這將在下一章作詳細(xì)介紹。
##JavaScript設(shè)置樣式
JavaScript有能力設(shè)置任何元素的樣式值,如例子中第29行所示,其原則是覆蓋樣式中同一屬性的值。大致的過程是,JavaScript引擎調(diào)用設(shè)置屬性值的公共處理函數(shù),然后該函數(shù)調(diào)用屬性值解析函數(shù),在這個(gè)例子中則是CSS的JS綁定函數(shù),而后將解析后的信息設(shè)置到元素的style屬性的樣式webkitTransform中,然后設(shè)置該元素需要重新計(jì)算style和invalidate它的style屬性,如下圖所示。
在這之后,重新繪制請(qǐng)求被處理時(shí),WebKit先會(huì)重新計(jì)算布局,而后在渲染相應(yīng)的區(qū)域。
## CSS3 變形(transform)、變換(transition)和動(dòng)畫(animation)
在WebKit渲染基礎(chǔ)中,我們介紹了DOM樹,Render樹和RenderLayer樹,根據(jù)WebKit的設(shè)計(jì)原則,可以知道上面HTML例子的三種樹結(jié)構(gòu)如下圖所示。也就是說,元素P的內(nèi)容會(huì)被包含在中間的RenderLayer中,Div元素的內(nèi)容會(huì)被會(huì)包含單獨(dú)的最下面的RenderLayer中,因?yàn)镈iv元素的需要做CSS的3D變形。
我們知道RenderLayer會(huì)有一個(gè)后端存儲(chǔ)空間,Chromium中是一個(gè)FrameBuffer,該層會(huì)轉(zhuǎn)成一個(gè)Texture,那些CSS3的變形,變換和動(dòng)畫效果將作用于該Texture上,而后繪制在網(wǎng)頁的framebuffer中。
變形比較簡(jiǎn)單,WebKit和Chromium內(nèi)部有一個(gè)Matrix來表示平移,旋轉(zhuǎn),縮放和扭曲等變形,因?yàn)镺penGL有直接對(duì)變形的支持,所以只需創(chuàng)建響應(yīng)的OpenGL變換操作即可。
變換和動(dòng)畫的實(shí)現(xiàn)大致相同,前面解析的過程跟其他屬性沒有區(qū)別,其結(jié)果會(huì)保存于Animation對(duì)象中。在chromium的實(shí)現(xiàn)中,CC(chromium compositor, 前面介紹過)中有兩個(gè)主要類對(duì)其提供支持,一個(gè)是CCLayerAnimationController,一個(gè)是CCActiveAnimation。前者是控制動(dòng)畫的行為,后者則是包含動(dòng)畫相關(guān)參數(shù)和狀態(tài)。這一部分其實(shí)很復(fù)雜,有興趣的讀者可以自行閱讀代碼。
## 源文件目錄
third_party/WebKit/Source/WebCore/css/
與CSS解析,內(nèi)部表示生成等一系列相關(guān)的類
third_party/WebKit/Source/WebCore/rendering/style
渲染所需要的樣式的支持類,其依賴于CSS解析器及其結(jié)果
## 參考文獻(xiàn)
1. http://www.w3schools.com/css
2. http://dev.w3.org/csswg/css3-regions/
By yongsheng@chromium.org
聯(lián)系客服