版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
理解WEBKIT和CHROMIUM目錄\h基礎(chǔ)篇\hWebKit,WebKit2,Chromium和Chrome介紹\hWebKit和Blink\hWebKit和Chromium代碼目錄結(jié)構(gòu)介紹\hWebKit和Chromium功能模塊\hChromium界面(UI)\hChromium多進(jìn)程模型\hChromium的多線程機(jī)制\h消息循環(huán)\h頁(yè)面渲染的基本過(guò)程\hHTML解析和DOM\hCSS基礎(chǔ)\hWebKit渲染基礎(chǔ)\h渲染主循環(huán)(mainloop)和requestAnimationFrame\hWebKit資源加載機(jī)制\hChromium的多進(jìn)程資源加載機(jī)制\hChromium網(wǎng)絡(luò)棧\hChromium資源磁盤緩存\hChromium插件和擴(kuò)展基礎(chǔ)\h高級(jí)篇\hChromium軟件渲染\hChromium的GPU硬件加速\hChromium硬件加速合成\h硬件加速之RenderLayer樹到合成樹\hCanvas2D及其實(shí)現(xiàn)\hWebGL及其實(shí)現(xiàn)\hWebKit的CSS實(shí)現(xiàn)\hWebKit布局(Layout)\h插件機(jī)制(NPAPIPlugin)\hContentAPI和CEF3\hChromium移動(dòng)版(ChromiumforMobile:Android&iOS)\hChromiumforAndroid\h基于Chromium內(nèi)核的AndroidWebView\hWeb應(yīng)用和Web運(yùn)行環(huán)境
基礎(chǔ)篇根據(jù)這些專題所涉及的內(nèi)容,大概把它們分為三個(gè)部分,第一個(gè)部分是基礎(chǔ)篇,第二部分是高級(jí)篇,第三部分是開放篇。WebKit,WebKit2,Chromium和Chrome介紹概述在介紹本系列各個(gè)專題之前,有必要先解釋一下極其容易混淆的幾個(gè)概念,它們是WebKit,WebKit2,Chromium和Chrome。首先來(lái)了解WebKit。廣義上來(lái)說(shuō),WebKit是一個(gè)開源的項(xiàng)目,其前身是來(lái)源于KDE的KHTML和KJS。該項(xiàng)目專注于網(wǎng)頁(yè)內(nèi)容的展示,開發(fā)出一流的網(wǎng)頁(yè)渲染引擎。它不是瀏覽器,而且也不想成為瀏覽器。該項(xiàng)目包含兩個(gè)部分,第一是WebCore,其中包含了對(duì)HTML,CSS等很多W3C規(guī)范的實(shí)現(xiàn);第二部分就是狹義上的WebKit,它主要是各個(gè)平臺(tái)的移植并提供相對(duì)應(yīng)的Web接口,也就是WebView或者類似WebView,這些接口提供操作和顯示網(wǎng)頁(yè)的能力。目前使用WebKit的主流的瀏覽器或者WebView包括Chrome,Safari,QtWebKit,AndroidBrowser以及眾多的移動(dòng)平臺(tái)的瀏覽器。WebKit2相對(duì)于狹義上的WebKit而言,它不是WebKit簡(jiǎn)單的第二個(gè)版本,它是一個(gè)新的API層,其最主要的變化在于將網(wǎng)頁(yè)的渲染置于單獨(dú)的進(jìn)程,而接口層則在另外一個(gè)進(jìn)程,它們之間通過(guò)IPC來(lái)通訊。對(duì)于接口的調(diào)用者來(lái)說(shuō),中間的IPC和底下的實(shí)現(xiàn)是透明的,這樣做的好處有很多,一個(gè)很明顯的好處是,當(dāng)網(wǎng)頁(yè)的渲染出現(xiàn)問(wèn)題時(shí),不會(huì)阻礙Web接口的調(diào)用者進(jìn)程,這會(huì)在很大程度上解決或者幫助解決瀏覽器或者這些調(diào)用者的穩(wěn)定性和安全性等問(wèn)題。Chromium是一個(gè)建立在WebKit之上的瀏覽器開源項(xiàng)目,由Google發(fā)起的。該項(xiàng)目被創(chuàng)建以來(lái)發(fā)展迅速,很多先進(jìn)的技術(shù)被采用,如跨進(jìn)程模型,沙箱模型等等。同時(shí),很多新的規(guī)范被支持,例如WebGL,Canvas2D,CSS3以及其他很多的HTML5特性,基本上每天你都可以看到它的變化,它的版本升級(jí)很快。在性能方面,其也備受稱贊,包括快速啟動(dòng),網(wǎng)頁(yè)加載迅速等。Chrome是Google公司的瀏覽器產(chǎn)品,它基于chromium開源項(xiàng)目,一般選擇穩(wěn)定的版本作為它的基礎(chǔ),它和chromium的不同點(diǎn)在于chromium是開源試驗(yàn)場(chǎng),會(huì)嘗試很多新的東西,當(dāng)這些東西穩(wěn)定之后,chrome才會(huì)集成進(jìn)來(lái),這也就是說(shuō)chrome的版本會(huì)落后于chromium。另外一個(gè)就是,chrome里面會(huì)加入一些私有的codec,這些僅在chrome中才會(huì)出現(xiàn)。再次,chrome還會(huì)整合Google的很多服務(wù),最后chrome還會(huì)有自動(dòng)更新的功能,這也是chromium所沒有的。參考文獻(xiàn)//wiki/WebKit2WebKit和Blink關(guān)注Web和HTML5領(lǐng)域的人最近應(yīng)該都有了解WebKit項(xiàng)目的重磅消息,那就是Google退出WebKit項(xiàng)目,創(chuàng)建自己的渲染引擎Blink。這其實(shí)不能說(shuō)完全沒有先兆,合合分分,純屬正常。其實(shí),之前關(guān)于WebKit2,雙方的爭(zhēng)論就非常的大。Apple希望它可以隨便加入和刪除代碼而無(wú)需擔(dān)心它會(huì)破壞其它Ports的代碼,這遭到很多人的反對(duì)和不滿。同時(shí),另一方面,Google有很多新的功能希望加入WebKit中,但是WebKit可能并不認(rèn)可他們。雙方分歧越來(lái)越多,終于分道揚(yáng)鑣。這里面有個(gè)誤區(qū),就是Google的Blink是一個(gè)全新的引擎。其實(shí)不是這樣,Blink目前就是從WebKit直接復(fù)制出一個(gè)版本出來(lái),然后將與chromium無(wú)關(guān)的Ports全部移除掉,將代碼結(jié)構(gòu)重新整理,就目前而言,Blink的渲染和WebKit是一樣,但是,以后兩者將各自走不同的路。這有點(diǎn)類似于之前WebKit從KHTML中復(fù)制出來(lái)一樣,歷史總是驚人的相似。目前參與Blink和Chromium大致一樣,擁有Chromium的commit權(quán)限對(duì)Blink也適用。原來(lái)一些WebKit的committer和reviewer也開始成為blink的committer。它的提交代碼流程,review流程等都是chromium的風(fēng)格,這對(duì)chromium的開發(fā)者來(lái)說(shuō)非常熟悉。Blink從WebKit繼承而來(lái),那么未來(lái)它會(huì)在哪些方面做改變呢?根據(jù)chromium官方的說(shuō)法,目前大概有兩個(gè)比較大的,后面應(yīng)該有更多的改變:跨進(jìn)程的iframe(out-of-processiframes):為iframes內(nèi)容創(chuàng)建單獨(dú)的沙箱進(jìn)程來(lái)渲染它們將DOM移入JavaScript中,這樣JavaScript可以更快的訪問(wèn)DOM今后,Blink會(huì)和WebKit差別越來(lái)越大,對(duì)Web標(biāo)準(zhǔn)支持也不盡相同,未來(lái)的發(fā)展如何,讓我們拭目以待吧。順便插一句,以后可能要改這個(gè)系列的標(biāo)題了,呵呵。參考資料/blinkWebKit和Chromium代碼目錄結(jié)構(gòu)介紹WebKit和Chromium的代碼量很大(這兩個(gè)項(xiàng)目都是幾百萬(wàn)行代碼的級(jí)別,不包括它們依賴的第三方庫(kù)),讀起來(lái)是相當(dāng)?shù)牟蝗菀?。但是良好的代碼組織結(jié)構(gòu)很好的幫助了開發(fā)者和學(xué)習(xí)者們,下面大致介紹一下它們的目錄結(jié)構(gòu)及其用處,方便了解和學(xué)習(xí),進(jìn)而快速地理解整個(gè)項(xiàng)目。因?yàn)槟夸泴?shí)在太多,所以這里介紹其中主要的部分。先來(lái)看看WebKit。(WebKit項(xiàng)目在chromium中的目錄是src/third_party/WebKit)再來(lái)看看Chromium。WebKit和Chromium功能模塊在“WebKit,WebKit2,Chromium和Chrome介紹”中,大致了解了WebKit是一個(gè)渲染引擎,Chromium是一個(gè)瀏覽器,它們那么分別包含哪些不同的功能模塊?它們是如何劃分地?本章節(jié)來(lái)為大家詳細(xì)解讀一下。WebKit:HTML解析:負(fù)責(zé)HTML語(yǔ)言的解析CSS解析:負(fù)責(zé)CSS的解析工作圖片解碼:支持不同編碼格式的圖片JavaScript引擎:JavaScript語(yǔ)言的解析引擎,缺省的是JavaScriptCore,但是目前Google的V8JavaScript被廣泛使用正則表達(dá)式布局:負(fù)責(zé)布局(layout)的計(jì)算和更新工作文檔對(duì)象模型(DOM):DOM是W3C定義的對(duì)象模型,該部分負(fù)責(zé)DOM樹及其相應(yīng)的接口渲染:與渲染相關(guān)的基礎(chǔ)設(shè)施,例如渲染樹,渲染層次樹等等SVG:對(duì)SVG的支持XML解析:XML語(yǔ)言的解析XSLT:XSLT語(yǔ)言的解析執(zhí)行URL解析器:URL規(guī)范的解析Unicode編解碼器:各種編碼解碼工作移植:WebKit中比較大的一部分,因?yàn)閃ebKit要工作需要不同平臺(tái)上有具體的實(shí)現(xiàn),因而不同的移植有不同的實(shí)現(xiàn)。chromium的移植很復(fù)雜,因?yàn)槠渲С挚缙脚_(tái),所以它的移植需要在windows,linux和mac上工作。由上面的模塊大致可以WebKit主要是跟網(wǎng)頁(yè)的解析和渲染相關(guān)的工作,其不涉及瀏覽器的歷史,書簽,下載,cookie管理等等方面的工作。Chromium:Cookie管理器:cookie生命周期的管理歷史管理器:歷史記錄的管理密碼管理器:網(wǎng)頁(yè)中密碼登錄信息管理窗口管理:多個(gè)Tab窗口的管理和切換地址欄:地址欄功能,智能地址填充與書簽的協(xié)同工作安全瀏覽黑名單管理:安全瀏覽機(jī)制網(wǎng)絡(luò)棧:與網(wǎng)絡(luò)傳輸相關(guān)的工作,其有很多創(chuàng)新的東西SSL/TLS:網(wǎng)絡(luò)傳輸安全磁盤緩存:磁盤緩存頁(yè)面及其替換策略等生命周期的管理下載管理器:管理下載相關(guān)粘帖板:clipboard的功能書簽管理:書簽的組織和管理URL解析器:同WebKitUnicode編解碼器:同WebKitChromium主要是實(shí)現(xiàn)瀏覽器相關(guān)的功能,如上面中的網(wǎng)絡(luò)棧等等。其實(shí)以上只是一些瀏覽器基本功能,chromium實(shí)現(xiàn)的遠(yuǎn)不止這些,這其中包含沙箱模型,NaCl,擴(kuò)展機(jī)制,硬件加速架構(gòu)等等。這些我們將在之后的章節(jié)中逐一介紹它們。URL解析器和Unicode編解碼器在兩者中都存在是因?yàn)樗鼈兌家褂玫?。Chromium界面(UI)Chromium的界面相當(dāng)簡(jiǎn)潔,這是她的設(shè)計(jì)理念。大體上可以把界面分成兩個(gè)主要部分:網(wǎng)頁(yè)內(nèi)容和外邊的修飾控件(例如,tab管理,工具欄,設(shè)置按鈕等)。整個(gè)chromium瀏覽器是個(gè)頂層窗口。每個(gè)tab都對(duì)應(yīng)一個(gè)頂層窗口的子窗口,每個(gè)網(wǎng)頁(yè)內(nèi)容都會(huì)繪制在一個(gè)子窗口中。當(dāng)然這個(gè)是現(xiàn)有的窗口結(jié)構(gòu),但在新的views框架中,窗口將會(huì)被移除,詳細(xì)的后面有專門介紹。Chromium界面另一個(gè)主要的控件是設(shè)置按鈕,里面包含了所有有關(guān)chromium屬性設(shè)置的部分。值得一提的是,里面有很多設(shè)置界面都是由HTML來(lái)撰寫的,而不是傳統(tǒng)的語(yǔ)言,例如c/c++。這很大程度上得益于chromium的擴(kuò)展機(jī)制及其提供的API,這會(huì)在擴(kuò)展章節(jié)詳細(xì)介紹。大家可能會(huì)覺得chromium界面簡(jiǎn)潔,用戶或者能看到的瀏覽器信息有限,其實(shí)不然。嘗試在地址欄里輸入chrome://chrome-urls/,你會(huì)看到很多的chrome地址。這些地址提供給用戶或者開發(fā)者關(guān)于瀏覽器的豐富的信息,可以說(shuō)是包羅萬(wàn)象,你能想象的信息基本都能從這里看到。這些信息其實(shí)非常的有用,特別對(duì)于理解chromium的內(nèi)部機(jī)制非常有幫助,很多細(xì)節(jié)我們會(huì)在后面的章節(jié)中逐一揭露。下面節(jié)選自chrome://chrome-urls/的輸出:ListofChromeURLschrome://appcache-internalschrome://blob-internalschrome://bookmarkschrome://cachechrome://chrome-urlschrome://crasheschrome://creditschrome://dnschrome://downloadschrome://extensionschrome://flagschrome://flash......Chromium多進(jìn)程模型概述相信你一定有這樣的經(jīng)歷:打開很多個(gè)頁(yè)面,不幸的是其中某個(gè)頁(yè)面不響應(yīng)了或者崩潰了,隨之而來(lái)的是更不幸的事,所有頁(yè)面都不響應(yīng)或者都崩潰了。最讓人崩潰的是其中一些頁(yè)面還有未保存或者未發(fā)送的信息!這絕對(duì)是不堪回首的過(guò)去。但是,現(xiàn)在好了,現(xiàn)代瀏覽器很多都支持多進(jìn)程模型,這個(gè)模型可以很好地避免上面的問(wèn)題,雖然它很復(fù)雜而且也有自身的問(wèn)題,例如更多的資源消耗,但是它的優(yōu)勢(shì)也是非常明顯地。chromium的多進(jìn)程架構(gòu)至少帶來(lái)三點(diǎn)好處,其一是避免單個(gè)頁(yè)面的不響應(yīng)或者奔潰影響整個(gè)瀏覽器的穩(wěn)定性;其二是當(dāng)?shù)谌讲寮紳r(shí)候不會(huì)影響頁(yè)面或者瀏覽器的穩(wěn)定性;其三是方便了安全模型的實(shí)施,也就是說(shuō)沙箱模型是基于多進(jìn)程架構(gòu)的。其實(shí),這很大程度上也是WebKit2產(chǎn)生的原因。那么,這是怎么做到的呢?下圖給出了缺省的chromium瀏覽器的進(jìn)程模型。方框代表進(jìn)程,連接線代表IPC進(jìn)程間通信。通常來(lái)講,chromium瀏覽器包括以下主要進(jìn)程類型:Browser進(jìn)程:瀏覽器的主進(jìn)程,負(fù)責(zé)瀏覽器界面的顯示,各個(gè)頁(yè)面的管理,其他各種進(jìn)程的管理;Render進(jìn)程:頁(yè)面的渲染進(jìn)程,負(fù)責(zé)頁(yè)面的渲染工作,WebKit的工作主要在這個(gè)進(jìn)程中完成;NPAPI插件進(jìn)程:每種類型的插件只會(huì)有一個(gè)進(jìn)程,每個(gè)插件進(jìn)程可以被多個(gè)Render進(jìn)程共享;GPU進(jìn)程:最多只有一個(gè),當(dāng)且僅當(dāng)GPU硬件加速打開的時(shí)候才會(huì)被創(chuàng)建,主要用于對(duì)3D加速調(diào)用的實(shí)現(xiàn);Pepper插件進(jìn)程:同NPAPI插件進(jìn)程,不同的是為Pepper插件而創(chuàng)建的進(jìn)程Chromium瀏覽器的進(jìn)程模型,包括以下特征:browser進(jìn)程和頁(yè)面是分開的,這保證了頁(yè)面的奔潰不會(huì)導(dǎo)致瀏覽器主界面的奔潰;每個(gè)頁(yè)面是獨(dú)立的進(jìn)程,這保證了頁(yè)面之間相互不影響;插件進(jìn)程也是獨(dú)立的,插件的問(wèn)題不會(huì)影響瀏覽器主界面和頁(yè)面;GPU硬件加速進(jìn)程也是獨(dú)立的。因?yàn)檫@么多的進(jìn)程,開發(fā)者通常需要知道進(jìn)程列表中的進(jìn)程類別,這很簡(jiǎn)單,可以通過(guò)進(jìn)程的命令行參數(shù)"--type"來(lái)識(shí)別。有趣的是,就在我寫下上面這段文字的時(shí)候,我的chrome瀏覽器的flash插件崩潰了,幸運(yùn)的是其他一切都很好,感謝chrome的多進(jìn)程模型!模型的類型其實(shí)介紹了進(jìn)程模型,其實(shí)Chromium支持多種進(jìn)程模型,特別是對(duì)頁(yè)面而言,下面簡(jiǎn)單的介紹以下模型的類型:Process-per-site-instance該類型的含義是對(duì)同一個(gè)域的實(shí)例都會(huì)創(chuàng)建獨(dú)立的進(jìn)程。舉個(gè)例子來(lái)講,例如,用戶訪問(wèn)了milado_nju的CSDN博客(我的博客),然后從個(gè)人主頁(yè)打開多篇文章時(shí),每篇文章的頁(yè)面都是該域的一個(gè)實(shí)例,因而它們都共享同一個(gè)的進(jìn)程。如果新打開CSDN博客的主頁(yè),那么就是另一個(gè)實(shí)例,會(huì)重新創(chuàng)建進(jìn)程來(lái)渲染它。這帶來(lái)的好處是每個(gè)頁(yè)面互不影響,壞處自然是資源的巨大浪費(fèi)。Process-per-site該類型的含義是不同一個(gè)域會(huì)創(chuàng)建獨(dú)立的進(jìn)程,同一域的不同實(shí)例共享同一個(gè)進(jìn)程。好處是對(duì)于不同的域可以共享,相對(duì)較小的內(nèi)存消耗,壞處是可能會(huì)有特別大的Renderer進(jìn)程??梢栽诿钚屑尤?yún)?shù)--process-per-site來(lái)嘗試它。Process-per-tab該類型的含義是為每個(gè)標(biāo)簽頁(yè)創(chuàng)建一個(gè)獨(dú)立的進(jìn)程,這也是chrome/chromium的缺省行為Singleprocess該類型的含義是不為頁(yè)面創(chuàng)建任何獨(dú)立的進(jìn)程,所有渲染工作都在browser進(jìn)程中。但是這個(gè)類型只是實(shí)驗(yàn)性質(zhì)的,不穩(wěn)定,因而不推薦使用,只有在比較單進(jìn)程和多進(jìn)程時(shí)候比較有用,可以在命令行加入?yún)?shù)--single-process來(lái)嘗試它。沙箱模型在頁(yè)面的多進(jìn)程模型中,頁(yè)面的渲染是運(yùn)行在沙箱模型中的Render進(jìn)程中實(shí)現(xiàn)的,這些渲染引擎沒有訪問(wèn)本地資源的能力(例如文件系統(tǒng),窗口系統(tǒng),等等),這可以保護(hù)渲染引擎被入侵。參考文獻(xiàn)/developers/design-documents/process-modelsChromium的多線程機(jī)制概述前面我們介紹過(guò)Chromium是基于多進(jìn)程模型的架構(gòu)設(shè)計(jì),那么各個(gè)進(jìn)程內(nèi)的情況呢?事實(shí)是每個(gè)進(jìn)程都有很多的線程,特別是browser進(jìn)程,因而它也基于多線程模型的。介紹多線程機(jī)制之前,先來(lái)看一下殘酷的現(xiàn)實(shí)吧,下面是各個(gè)進(jìn)程的線程信息情況(基于Linux平臺(tái),其它平臺(tái)的可能略有不同),相信保證讓你頭大。是的,你需要泡杯茶,然后靜下心來(lái)了解一下它們:為什么這么多的線程呢?Chromium的官方說(shuō)法告訴我們,主要目的就是為了保持UI的高響應(yīng)度,保證UI線程(chrome線程,主線程)不會(huì)被任何其它費(fèi)時(shí)的操作阻礙從而影響了對(duì)用戶的響應(yīng)。這些其它的操作很多,例如本地文件讀寫,socket讀寫,數(shù)據(jù)庫(kù)操作等等。既然它們會(huì)阻礙其它操作,那好,把它們放在單獨(dú)的線程里自己忙或者等待去吧,所以你就看到那么多與這些相關(guān)的線程(線程名顯然也暴露了這一切)。問(wèn)題來(lái)了,它們之間如何通信和同步呢?這是多線程的一個(gè)非常難纏的問(wèn)題,因?yàn)檫@會(huì)造成死鎖或者競(jìng)爭(zhēng)沖突等問(wèn)題。Chromium精心設(shè)計(jì)了一套機(jī)制來(lái)處理它們,那就是絕大多數(shù)的場(chǎng)景使用事件和一種chromium新創(chuàng)建的任務(wù)傳遞機(jī)制,僅在非用不可的情況下使用鎖或者線程安全對(duì)象,這有嚴(yán)格的要求,詳細(xì)的情況請(qǐng)查看以下鏈接以便作近一步的了解:/developers/lock-and-condition-variable。問(wèn)題又來(lái)了,那么每個(gè)線程內(nèi)部是如何處理這些事件和任務(wù)的呢?答案是MessageLoop。每個(gè)線程會(huì)有一個(gè)自己的MessageLoop,它們用來(lái)處理這些事件和任務(wù)。通常的MessageLoop只是處理事件,Chromium中的MessageLoop可以同時(shí)處理事件和任務(wù)。MessageLoop也是值得研究的,詳細(xì)情況我們將在以后的一章加以介紹。任務(wù)和MessageLoop的基本原理如下圖所示。任務(wù)被派發(fā)到進(jìn)程的某個(gè)線程的MessageLoop的隊(duì)列中,MessageLoop會(huì)調(diào)度執(zhí)行這些Task。關(guān)于上面這些線程,多數(shù)可以通過(guò)它們的名字猜出用途,這里鑒于篇幅和噪音考慮,不一一介紹,下面說(shuō)明幾個(gè)重要和詭異的線程:chrome線程:進(jìn)程的主線程,browser進(jìn)程重要主要是負(fù)責(zé)UI,當(dāng)然也是管家;Renderer進(jìn)程中則是管家兼處理WebKit渲染的;gpu進(jìn)程中則是負(fù)責(zé)處理處理繪圖請(qǐng)求并調(diào)用openGL進(jìn)行繪制工作的。Chrome_IOThread/Chrome_ChildIOThread線程:用來(lái)接受來(lái)自其它進(jìn)程的IPC消息和派發(fā)自身消息到其它進(jìn)程。SignalSender線程:V8JavaScript引擎中用于處理Linux信號(hào)的線程。任務(wù)(task)Chromium的特色就是在事件的基礎(chǔ)上,加入了一個(gè)新的機(jī)制-任務(wù)。當(dāng)需要執(zhí)行某個(gè)操作時(shí)候,可以把該操作封裝成一個(gè)任務(wù),由任務(wù)派發(fā)機(jī)制傳遞給相應(yīng)的進(jìn)程的MessageLoop。下面看看線程內(nèi)和線程間分別是如何操作的。首先看線程內(nèi)部是如何進(jìn)行的。但你需要進(jìn)行費(fèi)時(shí)的操作時(shí)候,可以派發(fā)一個(gè)事件和回調(diào)函數(shù)給自身線程的MessageLoop,然后MessageLoop會(huì)調(diào)度該回調(diào)函數(shù)以執(zhí)行其操作。問(wèn)題是這有必要嗎?直接調(diào)用不就可以嗎?答案是不可以,或者說(shuō)是最好不要這么做,其原因在于,如果當(dāng)前的MessageLoop里面有優(yōu)先級(jí)更高的事件和任務(wù)需要處理時(shí),你這樣做會(huì)阻礙它們的執(zhí)行。其次看一看線程間通信。假如一個(gè)線程A需要把任務(wù)傳遞給一個(gè)另外的線程B,大致有三個(gè)階段:首先,線程A把該任務(wù)傳遞給線程B;其次,線程B調(diào)度執(zhí)行該任務(wù);最后,線程B執(zhí)行完任務(wù)后回復(fù)A。很多情況下,線程A不需要回復(fù)。下圖描述的是一個(gè)帶回復(fù)的典型的任務(wù)傳遞過(guò)程。如果不需要回復(fù),那么上圖中的‘ReplyTask2’就不需要了。當(dāng)需要回復(fù)時(shí)候,chromium的做法是新建一個(gè)新的任務(wù),該任務(wù)封裝原來(lái)的任務(wù),新任務(wù)被放入線程B,B執(zhí)行新任務(wù)時(shí)候調(diào)用其Run方法,里面首先執(zhí)行原來(lái)的任務(wù),然后派發(fā)Reply任務(wù)給線程A,操作完成。后面會(huì)有一個(gè)具體的例子來(lái)描述上面這個(gè)過(guò)程,下面了解一下chromium支持Task所涉及的幾個(gè)主要類。Callback:回調(diào)類,其本質(zhì)是封裝了一個(gè)由調(diào)用者(例如線程A)設(shè)置的回調(diào)函數(shù)。包含一個(gè)run方法,當(dāng)MessageLoop調(diào)度執(zhí)行該Task時(shí)候,運(yùn)行該方法,該方法調(diào)用回調(diào)函數(shù)Closure:定義為Callback<void(void)>tracked_objects:一系列的類用于追蹤Task生成位置,編譯調(diào)試Task:包含一個(gè)Closure,追蹤信息,表示一個(gè)任務(wù)一個(gè)例子下面用一個(gè)實(shí)際的例子來(lái)理解線程間是如何傳遞任務(wù)的。該例子是chromium中非常常見的一個(gè)場(chǎng)景:browser進(jìn)程接收到來(lái)自renderer進(jìn)程的IPC消息,它由IO線程接收管理,然后派發(fā)給chrome線程處理,具體過(guò)程如下圖所示:上面的圖基本上對(duì)應(yīng)了前面的關(guān)于任務(wù)派發(fā)過(guò)程的圖,只是少了回復(fù)環(huán)節(jié),這是因?yàn)镮O線程并不需要回復(fù)。基本的步驟是,當(dāng)IO線程收到消息后,其派發(fā)一個(gè)任務(wù),該任務(wù)將ChannelProxy::Context::OnDispatchMessage設(shè)置會(huì)回調(diào)函數(shù),這個(gè)任務(wù)保存在chrome線程的輸入任務(wù)隊(duì)列中。chrome線程將輸入隊(duì)列拷貝到工作隊(duì)列后,執(zhí)行該任務(wù)的run方法,該方法會(huì)調(diào)用回調(diào)函數(shù)ChannelProxy::Context::OnDispatchMessage,該函數(shù)會(huì)調(diào)用事件的處理函數(shù),這里也就是RenderProcessHostImpl::OnMessageReceived,這個(gè)函數(shù)實(shí)際上會(huì)根據(jù)事件類型來(lái)調(diào)用各個(gè)相應(yīng)函數(shù)。源文件目錄base/threading/
線程相關(guān)的基礎(chǔ)類
參考文獻(xiàn)/developers/design-documents/threading/archives/478/developers/lock-and-condition-variable消息循環(huán)概述前面介紹了線程間如何傳遞chromium自定義任務(wù)(task),那么在線程內(nèi),消息循環(huán)(messageloop)是如何處理這所有的消息和任務(wù)呢?本章節(jié)重點(diǎn)介紹消息循環(huán)的工作原理。在Chromium里,需要處理三種類型的消息:chromium自定義的任務(wù),Socket或者文件等IO操作以及用戶界面(UI)的消息。這里面,chromium自定義任務(wù)是平臺(tái)無(wú)關(guān)的,而后面兩種類型的消息是平臺(tái)相關(guān)的?;貞浺幌虑懊娑嗑€程模型章節(jié)中列舉的眾多線程,例如主線程(UI線程)需要處理UI相關(guān)的消息和自定義任務(wù);IO線程則需要處理Socket和自定義任務(wù);History線程則只需要處理自定義任務(wù);其它線程需要處理消息類型不會(huì)超出以上三個(gè)線程。根據(jù)三個(gè)組合,chromium定義和實(shí)現(xiàn)三種相對(duì)應(yīng)的類來(lái)支持它們,下面讓我們來(lái)看看具體的實(shí)現(xiàn)吧。Chromium中主要的類這是本章中最重要的三個(gè)基類,依次介紹它們:類RunLoop:一個(gè)輔助類,主要封裝消息循環(huán)MessageLoop類,其本身沒有特別的功能,主要提供一組公共接口被調(diào)用,其實(shí)質(zhì)是調(diào)用MessageLoop類的接口和實(shí)現(xiàn)。類MessageLoop:主消息循環(huán),原理上講,它應(yīng)該可以處理三種類型的消息,包括支持不同平臺(tái)的消息。事實(shí)上,如果讓它處理所有這些消息,這會(huì)讓其代碼結(jié)構(gòu)復(fù)雜不清難以理解。因此,根據(jù)上面對(duì)各個(gè)線程的分析,消息循環(huán)也只需要三種類型,一種僅能處理自定義任務(wù),一種能處理自定義任務(wù)和IO操作,一種是能處理自定義任務(wù)和UI消息。很自然地,Chromium定義一個(gè)基類MessageLoop用于處理自定義任務(wù),兩個(gè)子類對(duì)應(yīng)于第二和第三種類型。如下圖所示。對(duì)于第二和第三種MessageLoop類型,它們除了要處理任務(wù)外,還要處理平臺(tái)相關(guān)的消息,為了結(jié)構(gòu)清晰,chromium定義一個(gè)新的基類及其子類來(lái)負(fù)責(zé)處理它們,這就是MessagePump。MessagePump的每個(gè)子類針對(duì)不同平臺(tái)和不同的消息類型。事實(shí)上,不僅如此,消息處理的主循環(huán)也在MessagePump中,這有些令人意外,后面會(huì)詳細(xì)介紹。因此,MessageLoop通過(guò)實(shí)現(xiàn)MessagePumpDelegate的接口來(lái)負(fù)責(zé)處理Chromium自定義任務(wù)。如下圖所示。類MessagePump:一個(gè)抽象出來(lái)的基類,可以用來(lái)處理第二和第三種消息類型。對(duì)于每個(gè)平臺(tái),它們有不同的MessagePump的子類來(lái)對(duì)應(yīng),這些子類被包含在MessageLoopForUI和MessageLoopForIO類中。結(jié)合上面的圖,針對(duì)三種類型的消息循環(huán),三種典型的線程在不同平臺(tái)所使用的類如下:對(duì)于所有平臺(tái)來(lái)說(shuō),History線程所使用的類是一樣的;UI線程和IO線程分別對(duì)應(yīng)不同的MessagePump。相信大家注意到了,最后一個(gè)平臺(tái)Android跟其它的稍有不同,那就是它的UI線程,原因在于主循環(huán)在Java層,用戶界面的事件派發(fā)機(jī)制都在Java代碼來(lái)處理,因而需要在Android的消息循環(huán)機(jī)制中加入對(duì)自定義任務(wù)的處理,后面會(huì)作介紹。無(wú)限循環(huán)這里面還有一個(gè)重要的部分需要介紹,那就是消息循環(huán)的主邏輯。該循環(huán)本質(zhì)就是一個(gè)無(wú)限循環(huán),不停的處理消息循環(huán)接收到的任務(wù)和消息,直到需要推出為止。如前面所述,主消息循環(huán)的邏輯在MessagePump中,而MessageLoop只是負(fù)責(zé)處理自定義的任務(wù),MessagePump通過(guò)調(diào)用MessagePumpDelegate來(lái)達(dá)到該目的。下面是IO線程的消息處理主邏輯的時(shí)序圖。前面章節(jié)我們介紹過(guò)MessageLoop有任務(wù)隊(duì)列來(lái)保存需要處理的任務(wù),這些任務(wù)可能有不同的優(yōu)先級(jí),例如需要即時(shí)處理,或者延遲處理,或者Idle時(shí)處理。上圖中,當(dāng)調(diào)用DoWork時(shí)候,首先將出入任務(wù)隊(duì)列(incomingtask)拷貝到工作任務(wù)隊(duì)列中,然后依次執(zhí)行該隊(duì)列中的任務(wù)。之后,同理處理調(diào)用DoDelayWork和DoIdleWork。當(dāng)這些處理完后,它會(huì)阻塞和等待在IO操作。對(duì)于Android的UI線程來(lái)說(shuō),情況稍有不同,因?yàn)橹餮h(huán)邏輯是由Java層的Android.os.Looper來(lái)控制的,所以MessagePumpAndroid其實(shí)是被Looper調(diào)用的。當(dāng)它被調(diào)用時(shí),其會(huì)把執(zhí)行任務(wù)的工作交給MessagePumpDelegate,這里也就是MessageLoopForUI來(lái)完成,如下圖所示的UI線程的消息循環(huán)主邏輯。Java層的SystemMessageHandler和C++層的MessagePumpAndroid其實(shí)都是輔助Looper調(diào)用執(zhí)行chromium的自定義類的。最后還有一個(gè)問(wèn)題,就是如何等待自定義的任務(wù)。假設(shè)現(xiàn)在MessageLoop沒有任務(wù)和消息需要處理,它應(yīng)該等待Socket或者IO或者任務(wù),OS系統(tǒng)支持Socket和IO喚醒它,問(wèn)題是如何等待自定義任務(wù)呢?總不能忙式的檢查吧,那樣太耗費(fèi)資源了。Chromium設(shè)計(jì)了一個(gè)巧妙的方法來(lái)解決該問(wèn)題,以MessagePumpLibEvent為例:在Linux平臺(tái)上,該類創(chuàng)建一個(gè)管道,它等待讀取這個(gè)管道的內(nèi)容,當(dāng)有自定義的新任務(wù)到來(lái)時(shí),寫入一個(gè)字節(jié)到這個(gè)管道,從而MessageLoop被喚醒,非常地簡(jiǎn)單和直接。源文件目錄base/message_loop.h|cc
base/message_pump.h|cc
消息循環(huán)相關(guān)的眾多類基本上都位于該目錄中,文件名以”message_”開頭
base/android/java/src/org/chromium/base/SystemMessageHandler.java
Android平臺(tái)下支持消息循環(huán)的輔助類
參考文獻(xiàn)/archives/478頁(yè)面渲染的基本過(guò)程概述前面介紹了一些渲染引擎的功能,包括網(wǎng)絡(luò),資源加載,DOM樹,RenderObject樹等等,但是,給人以零亂的感覺,因?yàn)闆]有一個(gè)整體的過(guò)程描述它們?cè)谶@個(gè)過(guò)程中的位置,它們只是整個(gè)渲染引擎工作的一個(gè)或者多個(gè)步驟而已。渲染引擎的主要目的就是從一個(gè)網(wǎng)頁(yè)的URL開始,經(jīng)過(guò)一系列的復(fù)雜處理過(guò)程之后,變成一個(gè)可視化的結(jié)果,這一過(guò)程就是這里所說(shuō)的頁(yè)面渲染的基本過(guò)程。所謂的渲染,就是根據(jù)描述或者定義構(gòu)建數(shù)學(xué)模型,通過(guò)模型生成圖像的過(guò)程。瀏覽器的渲染引擎就是能夠?qū)TML/CSS/JavaScript轉(zhuǎn)換成圖像結(jié)果的模塊,如下圖所示,輸入是URL對(duì)應(yīng)的各種資源,輸出是可視化的圖像。從這里看,非常的簡(jiǎn)單和容易理解。渲染模塊那么渲染引擎提供了哪些功能模塊來(lái)支持頁(yè)面渲染的呢?下圖是一個(gè)渲染引擎所包含的基本功能和它們依賴的一些第三方庫(kù)。從圖中大致可以看出,一個(gè)渲染引擎大致包括HTML解釋器,CSS解釋器,布局和JavaScript引擎。下面依次來(lái)描述它們:HTML解釋器:解釋HTML語(yǔ)言的解釋器,本質(zhì)是將HTML文本解釋成DOM(文檔對(duì)象模型)樹。CSS解釋器:解釋樣式表的解釋器,其作用是將DOM中的各個(gè)元素對(duì)象加上樣式信息,從而為計(jì)算最后結(jié)果的布局提供依據(jù)。布局:DOM之后,需要將其中的元素對(duì)象同樣式信息結(jié)合起來(lái),計(jì)算它們的大小位置等布局信息,形成一個(gè)能夠表示這所有信息的內(nèi)部表示模型。JavaScript引擎:JavaScript可以修改網(wǎng)頁(yè)的內(nèi)容,也能修改CSS的信息,JavaScript引擎解釋JavaScript代碼并把代碼的邏輯和對(duì)DOM和CSS的改動(dòng)信息應(yīng)用到布局中去,從而改變渲染的結(jié)果。這些模塊依賴很多其他的基礎(chǔ)模塊,這其中包括網(wǎng)絡(luò),存儲(chǔ),2D/3D圖形,音頻視頻和圖片解碼器等。實(shí)際上,渲染引擎中還應(yīng)該包括如何使用這些依賴模塊的部分,這部分的工作其實(shí)并不少,因?yàn)樾枰褂盟鼈儊?lái)高效的渲染網(wǎng)頁(yè)。例如,利用2D/3D圖形庫(kù)來(lái)實(shí)現(xiàn)高性能的網(wǎng)頁(yè)繪制和網(wǎng)頁(yè)的3D渲染,這個(gè)實(shí)現(xiàn)非常非常的復(fù)雜。最后,當(dāng)然,在最下面,依然少不了操作系統(tǒng)的支持,例如線程支持,文件支持等等?;具^(guò)程了解模塊之后,下面就是這些模塊如何組織以達(dá)成渲染過(guò)程的。一般地,一個(gè)典型的渲染過(guò)程下圖所示,這是渲染引擎的核心過(guò)程,一切都是圍繞著它來(lái)的。下面逐個(gè)從左至右來(lái)解釋上圖中的這一過(guò)程。這一過(guò)程的先后關(guān)系由圖中的實(shí)線箭頭表示。左上角開始,首先是網(wǎng)頁(yè)內(nèi)容,送到HTML解釋器。HTML解釋器在解釋它后形成DOM樹,中間如果遇到JavaScript代碼則交給JavaScript引擎去處理。如果頁(yè)面包含CSS,則交給CSS解釋器去解析。當(dāng)DOM建立的時(shí)候,接受來(lái)自CSS解釋的樣式信息,構(gòu)建一個(gè)新的內(nèi)部繪圖模型。該模型由布局模塊計(jì)算模型內(nèi)部的各個(gè)元素的位置和大小信息,最后由繪圖模塊完成從該模型到圖像的繪制。最后解釋圖中虛線箭頭的指向含義。它們表示在渲染過(guò)程中,每個(gè)階段可能使用到的其他模塊。在網(wǎng)頁(yè)內(nèi)容的下載中,需要使用到網(wǎng)絡(luò)和存儲(chǔ),這個(gè)是顯而易見地。但計(jì)算布局和繪圖的時(shí)候,需要使用2D/3D的圖形模塊,同時(shí)因?yàn)橐勺詈蟮目梢暬Y(jié)果,這時(shí)候需要開始解碼音頻視頻和圖片,同其它內(nèi)容一起繪制到最后的圖像中。在渲染完成之后,用戶可能需要跟渲染的結(jié)果進(jìn)行交互,或者網(wǎng)頁(yè)自身有動(dòng)畫,一般而言,這會(huì)持續(xù)的重新渲染過(guò)程。這個(gè)過(guò)程跟上面類似,不再贅述。參考資料/2013/webkit-for-developers/HTML解析和DOM概述前面介紹了很多眼花繚亂的新技術(shù),關(guān)于渲染,關(guān)于硬件加速,關(guān)于布局,關(guān)于其他很多,同大家一樣,我也花了很多時(shí)間來(lái)消化它們。本章介紹稍微基礎(chǔ)些的話題(本系列的寫作順序完全是隨心所欲地),就是在渲染整個(gè)過(guò)程的初始階段HTML解析。不過(guò)這不表示它簡(jiǎn)單,其實(shí)這里是非常繞人的。在前面描述渲染過(guò)程,其實(shí)也是回避了這些方面的很多細(xì)節(jié),原因也很簡(jiǎn)單,我自己也沒有完全仔細(xì)地了解清楚.:-(現(xiàn)在又重新閱讀和debug一下代碼,因此,借用本章節(jié),想完整的描述一下從HTML及其資源(例如圖片等)下載后到DOM樹建立好這一段過(guò)程的細(xì)節(jié)及其WebKit所涉及的這一切的一切。大體地,本章內(nèi)容主要包括網(wǎng)頁(yè)結(jié)構(gòu)描述,WebKit對(duì)網(wǎng)頁(yè)結(jié)構(gòu)的表示,WebKit解析HTML的基礎(chǔ)設(shè)施,一般過(guò)程,DOM規(guī)范和DOM樹。按照慣例,這里給出一個(gè)例子。個(gè)人崇尚簡(jiǎn)單,只要能說(shuō)明問(wèn)題就行,如下所示,后面的解釋和過(guò)程是基于此例子展開地。讓我們首先了解網(wǎng)頁(yè)的基本結(jié)構(gòu),這樣便于后面理解WebKit的相關(guān)設(shè)施。其實(shí),它并不復(fù)雜,如下圖所示。整個(gè)是一個(gè)網(wǎng)頁(yè),這里稱之為Page。每個(gè)Page都有一個(gè)主框(MainFrame),該框通常包含一個(gè)HTMLDocument,主框也可能包含子框(subframe),如圖所示,這就是網(wǎng)頁(yè)的多框結(jié)構(gòu),雖然這一結(jié)構(gòu)飽受詬病,但現(xiàn)實(shí)還是這樣?,F(xiàn)在,更多的網(wǎng)頁(yè)僅有主框,這樣便于搜索引擎的檢索和處理。本章的例子是一個(gè)僅有主框的最簡(jiǎn)單的網(wǎng)頁(yè)結(jié)構(gòu)。這些框構(gòu)成一個(gè)樹型結(jié)構(gòu),以主框?yàn)楦?jié)點(diǎn),每個(gè)框也可能包含自己的HTMLDocument,它是一顆DOM樹。WebKit設(shè)施介紹了網(wǎng)頁(yè)的基本結(jié)構(gòu),那么WebKit的設(shè)施也相對(duì)容易理解,下圖是WebKit相對(duì)應(yīng)的類,和網(wǎng)頁(yè)的結(jié)構(gòu)是一一對(duì)應(yīng)的。這其中WebView是網(wǎng)頁(yè)對(duì)外的接口。在介紹WebKit中其他類之前,有必要先介紹一個(gè)WebKit普遍使用的設(shè)計(jì)模式。首先看一個(gè)例子。下圖是Chrome和ChromeClient,這兩個(gè)類非常重要。此Chrome非彼Chrome,這里的Chrome是WebKit的一個(gè)類,不是Google的瀏覽器產(chǎn)品Chrome。類Chrome后面會(huì)介紹,這里強(qiáng)調(diào)的是因?yàn)閃ebKit有很多種移植(port),所以不同移植中的Chrome需要有不同的實(shí)現(xiàn),所以Chrome類需要滿足兩類要求:1)Chrome需要具備有獲取各個(gè)平臺(tái)資源的能力,例如WebKit可以調(diào)用Chrome來(lái)創(chuàng)建一個(gè)新窗口;2)Chrome需要把WebKit的狀態(tài)和進(jìn)度等信息分發(fā)給外部的調(diào)用者或者說(shuō)是WebKit的使用者;WebKit內(nèi)部跟這兩類要求相關(guān)的需求都是通過(guò)Chrome的接口來(lái)完成,這時(shí)候有個(gè)問(wèn)題,那就是如何讓W(xué)ebKit和外部調(diào)用者既不緊密耦合,而能方便支持不同的平臺(tái)?WebKit使用ChromeClient抽象類來(lái)實(shí)現(xiàn)。每個(gè)port實(shí)現(xiàn)類ChromeClient,一方面監(jiān)聽WebKit狀態(tài),一方面返回WebKit所需要的資源和信息。WebKit直接調(diào)用Chrome的接口,Chrome調(diào)用ChromeClient的接口,而ChromeClient的實(shí)現(xiàn)由各個(gè)移植來(lái)完成。在這一部分中,很多都是該模式的類組合,例如FrameLoader和FrameLoaderClient,ImageLoader和ImageLoaderClient,ContextMenu和ContextMenuClient等等。這些類比較容易理解,不再闡述,這里簡(jiǎn)單介紹幾組類的關(guān)系(很多其他的組類似):Frame和FrameLoader:Frame表示的是頁(yè)面框和框的加載器,一個(gè)負(fù)責(zé)頁(yè)面的表示,一個(gè)負(fù)責(zé)加載需要的接口及實(shí)現(xiàn),還有很多類似的組合類,例如Document和DocumentLoader,CachedImage和ImageLoader等。WebView和Page:二者一一對(duì)應(yīng),Page是WebKit內(nèi)部表示網(wǎng)頁(yè)的類,WebView是WebKit對(duì)外表示網(wǎng)頁(yè)的類,Page只有一個(gè)實(shí)現(xiàn),WebView在不同的移植中有不同的實(shí)現(xiàn)。其他類似的組合類如WebFrame和Frame。最后接上面介紹的類Chrome,它是一個(gè)非常重要的類,是WebKit與它的使用者之間的橋梁,主要負(fù)責(zé)UI和渲染相關(guān)的需要用到平臺(tái)相關(guān)接口。以下是它的一些主要功能:跟UI和渲染顯示相關(guān)的需要移植實(shí)現(xiàn)的接口集合類;繼承自HostWindow(宿主窗口),其包含一系列接口,用來(lái)通知重繪或者更新相應(yīng)整個(gè)窗口,滾動(dòng)窗口等等;窗口相關(guān)操作,例如顯示,隱藏等;顯示/隱藏窗口中的toolbar,statusbar,scroolbar等;顯示JavaScript相關(guān)的窗口,例如JavaScript的Alert,confirm,prompt窗口等;其他一些跟顯示相關(guān)的,例如colorchooser等。HTML解析的一般過(guò)程首先介紹主要對(duì)象的創(chuàng)建順序,如下圖所示,簡(jiǎn)單明了。下面是解析和創(chuàng)建DOM樹的一般過(guò)程,依舊是用圖說(shuō)話,為簡(jiǎn)單起見,省略其中的若干類和步驟。上述圖中的順序,簡(jiǎn)單來(lái)講是,先接受服務(wù)器發(fā)送過(guò)來(lái)的數(shù)據(jù),之后解析成一系列的Tokens,然后在加載結(jié)束后建立DOM樹,這還是比較容易理解地,不再詳細(xì)描述。下圖是解析完例子后所創(chuàng)建的DOM樹。簡(jiǎn)單吧?我也覺得:-)DOM標(biāo)準(zhǔn)DOM定義的是一組平臺(tái)無(wú)關(guān)和語(yǔ)言無(wú)關(guān)的接口,該接口允許編程語(yǔ)言動(dòng)態(tài)訪問(wèn)和更改結(jié)構(gòu)化文檔。本人(:-))見的比較多的應(yīng)用對(duì)象是XML和HTML。W3C標(biāo)準(zhǔn)化組織定義一系列DOM接口,隨著時(shí)間的發(fā)展,目前包括DOMlevel1,2,3。每個(gè)新版本是對(duì)以前版本的補(bǔ)充和新功能的加入。下面是對(duì)這三個(gè)版本的簡(jiǎn)單介紹:DOMlevel1:1.Core:一組底層的接口,其接口可以表示任何結(jié)構(gòu)化的文檔,同時(shí)也允許對(duì)其進(jìn)行擴(kuò)展,典型的例子是支持XML文檔。2.HTML:一組基于Core定義的接口的上層接口,主要是為了方便HTML文檔的訪問(wèn)。DOMlevel2:1.Core:對(duì)DOMlevel1中core部分的擴(kuò)展,其中著名的就是getElementById(沒用過(guò)的請(qǐng)舉手),還有很多跟名空間(namespace)相關(guān)的接口;2.Views:書上說(shuō)views是“允許動(dòng)態(tài)訪問(wèn)和修改文檔內(nèi)容的表示,主要是兩個(gè)接口AbstractView和DocumentView”,沒接觸過(guò)加不懂,誰(shuí)來(lái)救救我?3.Events:這個(gè)很重要,引入了對(duì)時(shí)間的處理,個(gè)人覺得是個(gè)重要的變化,主要有EventTarget,Mouseevents等接口,但不支持Keyboard,這個(gè)在DOMlevel3才被加入;4.Style(CSS):加入接口可以修改樣式屬性;5.Traversalandrange:這個(gè)容易理解,就是遍歷樹(NodeIterator和TreeWalker)加上對(duì)制定范圍的文檔修改刪除等操作;6.HTML:擴(kuò)充DOMlevel1的HTML部分,允許動(dòng)態(tài)訪問(wèn)和修改HTML文檔。DOMlevel3:1.Core:加入了新的adoptNode()和textContent支持;2.Loadandsave:動(dòng)態(tài)加載和序列化DOM表示;3.Validation:根據(jù)scheme驗(yàn)證文檔的有效性;4.Events:主要擴(kuò)展對(duì)keyboard的支持。HTML5和觸屏技術(shù)如火如荼,所以對(duì)Touch的支持很快就會(huì)進(jìn)入規(guī)范;5.XPath:使用XPath1.0來(lái)訪問(wèn)DOM樹,XPath是一種簡(jiǎn)單直觀的檢索DOM樹節(jié)點(diǎn)的方式,具體見W3CXPath標(biāo)準(zhǔn)。DOM標(biāo)準(zhǔn)對(duì)具體的表示方法沒有任何限制,只是定義了接口,因此它在現(xiàn)實(shí)中有很多種實(shí)現(xiàn)。DOM的樹狀表示是其中比較普遍的方式,還可以是二進(jìn)制表示,其有很多的優(yōu)點(diǎn)(例如binaryxml),詳情請(qǐng)google之。好了,該結(jié)束本章了,但這不是全部,這部分還有很多的細(xì)節(jié)值得研究和探討,例如個(gè)人比較關(guān)注的是DOM樹上的事件(Event)分發(fā)機(jī)制。有興趣的話,可以自行閱讀以下目錄中的WebKit代碼,相信你會(huì)獲益非淺。源文件目錄third_party/WebKit/Source/WebCore/loader
加載相關(guān)的類,例如負(fù)責(zé)加載Document,F(xiàn)rame,image等
third_party/WebKit/Source/WebCore/page
表示頁(yè)面的類,例如page,frame等,及頁(yè)面相關(guān)的設(shè)施,例如contextmenu,JavaScript內(nèi)置的對(duì)象,例如,window,console,navigator,dom等。
third_party/WebKit/Source/WebCore/dom
DOM相關(guān)的類,包括節(jié)點(diǎn)定義類
third_party/WebKit/Source/WebCore/html
html標(biāo)注的相應(yīng)的DOM節(jié)點(diǎn)類定義
third_party/WebKit/Source/WebCore/html/parser
HTML解析器相關(guān)類
參考文獻(xiàn)/DOM//wiki/Document_Object_Model/TR/DOM-Level-3-Events//TR/DOM-Level-3-Core/core.html/htmldom/default.asp/en-US/docs/DOM_Levels/developerworks/cn/xml/x-keydom//developerworks/cn/xml/x-keydom2/index.html/view/7fa3ad6e58fafab069dc02b8.htmlCSS基礎(chǔ)CSS初探概述先談?wù)凥TML網(wǎng)頁(yè)的開發(fā)者們所遭遇地痛苦和悲慘的經(jīng)歷。在CSS出現(xiàn)前或者出現(xiàn)早期,HTML因?yàn)橐O(shè)計(jì)不同風(fēng)格和樣式的元素,所以在不停地加入很多新的元素來(lái)表示,例如p,span。然后,問(wèn)題還是存在,那就是大量的使用表格(Table)元素來(lái)排列網(wǎng)頁(yè)中的元素,這導(dǎo)致一些不好的問(wèn)題,其一,Table經(jīng)常嵌Table,導(dǎo)致網(wǎng)頁(yè)較大,消耗帶寬,其二,被搜索引擎解析后,其內(nèi)容變得雜亂無(wú)章。慶幸地是,CSS的出現(xiàn)極大地解決了這些問(wèn)題。CSS的全稱是CascadingStyleSheet,中文名是級(jí)聯(lián)樣式表,是用來(lái)控制網(wǎng)頁(yè)顯示風(fēng)格的,被廣泛地使用在網(wǎng)頁(yè)中,現(xiàn)在基本所有的瀏覽器支持它。其有一個(gè)比較重要的特征就是將網(wǎng)頁(yè)的內(nèi)容和展示的方式分離開,這很重要。另一個(gè)重要的特征是它很強(qiáng)大,不是一般的強(qiáng)大,特別是新的CSS3標(biāo)準(zhǔn),不僅能提供對(duì)頁(yè)面各個(gè)元素的精準(zhǔn)控制,同時(shí)提供豐富多彩的樣式。簡(jiǎn)而言之,CSS是一種非常出色的文本展示語(yǔ)言。本章將簡(jiǎn)單介紹CSS的一些基本功能,讓你對(duì)它有個(gè)大概地認(rèn)識(shí),后面的一章中,我們將會(huì)介紹它們?nèi)绾卧赪ebKit和chormium中獲得支持地。下面給出一個(gè)雖然簡(jiǎn)單但是卻展示了CSS眾多特征的示例,CSS的主要部分在包含元素Style中,也就是下例中從第3行到第16行,同時(shí)JavaScript中也有使用部分對(duì)樣式的操作,后面的部分會(huì)對(duì)它們逐一加以解釋。CSS功能選擇器CSS的選擇器是一組模式,用來(lái)匹配相應(yīng)的HTML元素。但選擇器匹配相應(yīng)的元素時(shí)候,該選擇器包含的各種樣式的設(shè)置就會(huì)作用在選中的元素上。通過(guò)選擇器,CSS能夠精準(zhǔn)地控制HTML頁(yè)面中的任意一個(gè)或者多個(gè)元素的屬性??瓷厦娴睦又械谒男?。該行中的’div’就是一個(gè)選擇器,它屬于元素選擇器,其含義是選擇該頁(yè)面中的所有’div’元素。因?yàn)閮H有第20行包含一個(gè)’div’元素,所以,該選擇的選擇結(jié)果就是該元素。那么,div下面所設(shè)置的樣式等屬性(花括號(hào)內(nèi))都會(huì)作用在該元素,從第6行到第16行。示例中的選擇器僅是眾多選擇器類型中的一種,從CSS1到CSS3,規(guī)范陸續(xù)地加入了多達(dá)42種選擇器,極大地方便了開發(fā)者,下面介紹其中一些主要的選擇器:標(biāo)簽選擇器:根據(jù)元素的名稱來(lái)選擇,例如例子中的選擇器,可以選擇一個(gè)或者多個(gè)類選擇器:根據(jù)類別信息來(lái)選擇目標(biāo)元素,可以選擇一個(gè)或者多個(gè),例子中選擇div元素也可以-使用類選擇器,方法是”.aclass”;ID選擇器:根據(jù)ID來(lái)選擇目標(biāo)元素,僅能選擇一個(gè),例子中選擇div元素也可以根據(jù)屬性選擇器,方法可以是”#adiv”屬性選擇器:根據(jù)屬性來(lái)選擇目標(biāo)元素,可以選擇一個(gè)或者多個(gè),例子中選擇div元素也可以使用屬性選擇器,方法是”div[id]”,”div[id=’adiv’]”,”div[id~=’di’]”,”div[id|=’ad’]”;后代選擇器:選擇某元素包含的后代元素,可以選擇一個(gè)或者多個(gè),例子中選擇div元素也可以使用后代選擇器,方法是”bodydiv”;子女選擇器:選擇某元素包含的子女元素,可以選擇一個(gè)或者多個(gè),例子中選擇div元素也可以使用后代選擇器,方法是”body>div”;相鄰?fù)x擇器:根據(jù)相鄰?fù)畔?lái)確定選擇的元素,可以選擇一個(gè)或者多個(gè),例子中選擇div元素也可以使用相鄰?fù)x擇器,方法是”p+div”;還有很多類型的選擇器,例如偽類選擇器,通用選擇器,群組選擇器,根選擇器等等,這里不再一一作介紹。介紹了選擇器之后,后面還有個(gè)重要的問(wèn)題,那就是優(yōu)先級(jí)。因?yàn)槎鄠€(gè)選擇器可能作用于同一個(gè)元素,它們?cè)O(shè)置的樣式屬性可能不一樣,這種情況下,應(yīng)該怎么確定使用哪種樣式。一般而言,選擇器越特殊,它的優(yōu)先級(jí)越高,也就是所選擇器指向的越準(zhǔn)確,它的優(yōu)先級(jí)就越高。例如,如果用1表示標(biāo)簽選擇器的優(yōu)先級(jí),那么類選擇器優(yōu)先級(jí)是10,id選擇器就是100,數(shù)值越大表示優(yōu)先級(jí)別越高。所以,盡量使用精確控制的選擇器,使用合理優(yōu)先級(jí)的選擇。各種屬性從第6行到第16行設(shè)置選擇的元素的樣式屬性值,大致把這些屬性分成以下類別:背景:通常有兩種方式設(shè)置,一個(gè)是設(shè)置背景顏色(例子中的background-color),另外一種設(shè)置背景圖片。文本:設(shè)置文本縮進(jìn),對(duì)齊,單詞間隔,字母間隔,字符轉(zhuǎn)換,裝飾,空白字符等字體:設(shè)置字體屬性,可以是內(nèi)嵌的,也可以是自定義的方式,另外還可以設(shè)置加粗,變形等等。列表:設(shè)置列表類型,可以以字母,希臘字母,數(shù)字等變好列表表格:通過(guò)設(shè)置邊框來(lái)達(dá)到表格的目的,設(shè)置是否把表格邊框合并為單一的邊框,設(shè)置分隔單元格邊框的距離,設(shè)置表格標(biāo)題的位置,設(shè)置是否顯示表格中的空單元格,設(shè)置顯示單元、行和列的算法等??蚰P?boxmodel):框模型定義了元素框處理元素內(nèi)容,內(nèi)邊框,邊框和外邊距處理方式。在示例中包含屬性’border’,’padding’分別表示邊框和內(nèi)邊距。定位:CSS提供相對(duì),絕對(duì)地位和浮動(dòng)定位。示例使用了絕對(duì)定位,參見第6到第8行。從示例中相信你可以看到,CSS的基本單元就是選擇器加上它所包含的各個(gè)屬性設(shè)置,參看示例中第4行到第17行,CSS就是由多個(gè)這樣的基本單元所組成。在編寫CSS的時(shí)候,通過(guò)選擇器來(lái)精確控制需要選擇的元素,然后通過(guò)設(shè)置屬性值來(lái)讓這些元素展示出不同的顯示效果。CSS3新增功能選擇器:上面介紹屬性選擇器就是CSS3新加入的,除此之外,還加入了精確控制的選擇器用來(lái)選擇特定位置的子女,特定元素標(biāo)簽的子女等等。樣式:增加了一些比較好的功能,例如自定義字體,圓角屬性,邊框顏色等等變換,過(guò)渡和動(dòng)畫(transform,transition,animation):CSS3提供令人驚奇的變好,轉(zhuǎn)變和動(dòng)畫功能,另其更加的賞心悅目。規(guī)范的草案中定義了2D的變換,更為吃驚的是WebKit提供了3D的變換。變換有三種類型,平移,旋轉(zhuǎn)和縮放。同2D不同的是,3D增加了繞Z軸的平移旋轉(zhuǎn)和縮放。有一點(diǎn)頗令人遺憾,那就是各個(gè)不同的瀏覽器對(duì)這些屬性的名字定義不一致,例如標(biāo)準(zhǔn)對(duì)變換的定義屬性名是’transform’,而webkit的是’-webkit-transform’,如例子中第15行所示,IE的’-ms-transform’,firefox的則是’-moz-transform’,opera的是’-o-transform’,這不免令人心煩意亂。過(guò)渡(transition)描述了屬性從一個(gè)值過(guò)渡到另一個(gè)值的過(guò)程,定義了過(guò)程的時(shí)間,啟動(dòng)過(guò)程的延遲時(shí)間等等。但是,這些標(biāo)準(zhǔn)草案中的定義還不足以描述更精確的變化過(guò)程,所以引入了更為靈活的方式,這就是CSS動(dòng)畫(animation)。通過(guò)動(dòng)畫,你能夠定義不同的keyframes來(lái)控制中間變化過(guò)程而不僅僅是開始和結(jié)束。你可以這么理解,過(guò)渡是一種較為簡(jiǎn)單和常見的動(dòng)畫。CSS和JavaScript在新的瀏覽器中,Javascript也可以方便且簡(jiǎn)單地來(lái)操作設(shè)置css的值,看看例子第24行到34行的代碼。在函數(shù)’loop’中,但得到元素’adiv’后,可以通過(guò)設(shè)置它的style屬性來(lái)設(shè)置各個(gè)CSS的屬性值,例如本例中是要設(shè)置變換的不同角度,含義是通過(guò)不斷地改變’webkitTransform’的值來(lái)讓’adiv’元素繞Z軸旋轉(zhuǎn)起來(lái),效果相當(dāng)酷。另外一個(gè)非常不錯(cuò)的功能是,規(guī)范中引入了兩個(gè)新的JavaScript接口:querySelector和querySelectorAll。這兩個(gè)接口讓CSS定義的所有的選擇器都可以作為參數(shù)傳給這兩個(gè)接口,從而獲取到相應(yīng)的HTML頁(yè)面中的元素,這非常的有用,你值得試試。chromium,safari和Firefox都支持它。在這一節(jié)結(jié)束前,強(qiáng)烈建議你將該示例在瀏覽器中嘗試(最好是chrome或者safari,如果是其它瀏覽器,可能需要做相應(yīng)的屬性名修改),同時(shí),逐一注釋掉每個(gè)屬性,看看它怎么影響最終的顯示效果。參考文獻(xiàn)/projects/layout/index.html/wiki/Tableless_web_design/cssref/css_selectors.aspWebKit渲染基礎(chǔ)概述WebKit是一個(gè)渲染引擎,而不是一個(gè)瀏覽器,它專注于網(wǎng)頁(yè)內(nèi)容展示,其中渲染是其中核心的部分之一。本章著重于對(duì)渲染部分的基礎(chǔ)進(jìn)行一定程度的了解和認(rèn)識(shí),主要理解基于DOM樹來(lái)介紹Render樹和RenderLayer樹的構(gòu)建由來(lái)和方式。那么什么是DOM?簡(jiǎn)單來(lái)說(shuō),DOM是對(duì)HTML或者XML等文檔的一種結(jié)構(gòu)化表示方法,通過(guò)這種方式,用戶可以通過(guò)提供標(biāo)準(zhǔn)的接口來(lái)訪問(wèn)HTML頁(yè)面中的任何元素的相關(guān)屬性,并可對(duì)DOM進(jìn)行相應(yīng)的添加、刪除和更新操作等。相關(guān)信息可查閱W3C的文檔,這里不再贅述?;贒OM樹的一些可視(visual)的節(jié)點(diǎn),WebKit來(lái)根據(jù)需要來(lái)創(chuàng)建相應(yīng)的RenderObject節(jié)點(diǎn),這些節(jié)點(diǎn)也構(gòu)成了一顆樹,稱之為Render樹。基于Render樹,WebKit也會(huì)根據(jù)需要來(lái)為它們中的某些節(jié)點(diǎn)創(chuàng)建新的RenderLayer節(jié)點(diǎn),從而形成一棵RenderLayer樹。Render樹和RenderLayer樹是WebKit支持渲染所提供的基礎(chǔ)但是卻非常重要的設(shè)施。這是因?yàn)閃ebKit的布局計(jì)算依賴它們,瀏覽器的渲染和GPU硬件加速也都依賴于它們。幸運(yùn)地是,得益于它們接口定義的靈活性,不同的瀏覽器可以很方便地來(lái)實(shí)現(xiàn)自己的渲染和加速機(jī)制。為了直觀了解這三種樹,下圖給出了這三種樹及其它們之間的對(duì)應(yīng)關(guān)系,后面會(huì)詳細(xì)介紹里面的細(xì)節(jié)。Render樹Render樹的建立Render樹是基于DOM樹建立起來(lái)的一顆新的樹,是布局和渲染等機(jī)制的基礎(chǔ)設(shè)施。Render樹節(jié)點(diǎn)和DOM樹節(jié)點(diǎn)不是一一對(duì)應(yīng)關(guān)系,那么哪些情況下需要建立新的Render節(jié)點(diǎn)呢?a)DOM樹的document節(jié)點(diǎn);b)DOM樹中的可視化節(jié)點(diǎn),例如HTML,BODY,DIV等,非可視化節(jié)點(diǎn)不會(huì)建立Render樹節(jié)點(diǎn),例如HEAD,META,SCRIPT等;c)某些情況下需要建立匿名的Render節(jié)點(diǎn),該節(jié)點(diǎn)不對(duì)應(yīng)于DOM樹中的任何節(jié)點(diǎn);RenderObject對(duì)象在DOM樹創(chuàng)建的同時(shí)也會(huì)被創(chuàng)建,當(dāng)然,如果DOM中有動(dòng)態(tài)加入元素時(shí),也可能會(huì)相應(yīng)地創(chuàng)建RenderObject對(duì)象。下圖示例的是RenderObject對(duì)象被創(chuàng)建的函數(shù)調(diào)用過(guò)程。Render樹建立之后,布局運(yùn)算會(huì)計(jì)算出相關(guān)的屬性,這其中有位置,大小,是否浮動(dòng)等。有了這些信息之后,渲染引擎才只知道在何處以及如何畫這些元素。RenderObject類及其子類RenderObject是Render樹的節(jié)點(diǎn)基礎(chǔ)類,提供了一組公共的接口。它有很多的子類,這些子類可能對(duì)應(yīng)一些DOM樹中的節(jié)點(diǎn),例如RenderText,有些則是容器類,例如RenderBlock。下圖給出了一些常用的類的繼承關(guān)系圖,這其中RenderBlock是一個(gè)非常重要的類。匿名RenderBlock對(duì)象CSS中有塊級(jí)元素和內(nèi)嵌(inline)元素之分。內(nèi)嵌元素表現(xiàn)的是行布局形式,就是說(shuō)這些元素以行進(jìn)行顯示。以’div’元素為例,如果設(shè)置屬性’style’為’display:inline’時(shí),則那是內(nèi)嵌元素,那么它可能與前面的元素在同一行;如果該元素沒有設(shè)置這個(gè)屬性時(shí),則是塊級(jí)元素,那么在新的行里顯示。RenderBlock用來(lái)是用來(lái)表示塊級(jí)元素,為了處理上的方便,某些情況下需要建立匿名的RenderBlock對(duì)象,因?yàn)镽enderBlock的子女必須都是內(nèi)嵌的元素或者都是非內(nèi)嵌的元素。所以,當(dāng)它包含兩種元素的時(shí)候,那么它會(huì)為相鄰的內(nèi)嵌元素創(chuàng)建一個(gè)塊級(jí)RenderBlock節(jié)點(diǎn),然后設(shè)置該節(jié)點(diǎn)為自己的子女并且設(shè)置這些內(nèi)嵌元素為它的子女。RenderLayer樹RenderLayer樹是基于Render樹建立起來(lái)的一顆新的樹。同樣,RenderLayer節(jié)點(diǎn)和Render節(jié)點(diǎn)不是一一對(duì)應(yīng)關(guān)系,而是一對(duì)多的關(guān)系。那么哪些情況下的RenderObject節(jié)點(diǎn)需要建立新的RenderLayer節(jié)點(diǎn)呢?a)DOM樹的document節(jié)點(diǎn)對(duì)應(yīng)的RenderView節(jié)點(diǎn)b)DOM樹中的document的子女節(jié)點(diǎn),也就是HTML節(jié)點(diǎn)對(duì)應(yīng)的RenderBlock節(jié)點(diǎn)c)顯式的CSS位置d)有透明效果的對(duì)象e)節(jié)點(diǎn)有溢出(overflow),alpha或者反射等效果的f)Canvas2D和3D(WebGL)g)Video節(jié)點(diǎn)對(duì)應(yīng)的RenderObject對(duì)象一個(gè)RenderLayer被建立之后,其所在的RenderObject對(duì)象的所有后代均包含在該RenderLayer,除非這些后代需要建立自己的RenderLayer。而后代的RenderLayer的父親就是自己最近的祖先所在的不同的RenderLayer,這樣,它們也構(gòu)成了一顆RenderLayer樹。每個(gè)RenderLayer對(duì)應(yīng)的Render節(jié)點(diǎn)內(nèi)容均會(huì)繪制在該RenderLayer所對(duì)應(yīng)的層次上(或者內(nèi)部存儲(chǔ)結(jié)構(gòu)上)。不同的RenderLayer可以共享同一個(gè)內(nèi)部存儲(chǔ)結(jié)構(gòu),也可以有各自的后端存儲(chǔ),這取決于不同的移植。在軟件渲染下,通常各個(gè)RenderLayer的內(nèi)容都繪制在同一塊后端存儲(chǔ)上。在GPU硬件加速的下,某些RenderLayer可能有自己獨(dú)立的后端存儲(chǔ),而后通過(guò)合成器來(lái)把這些不同的后端合成在一起,最終形成網(wǎng)頁(yè)的可視化內(nèi)容。RenderLayer在創(chuàng)建RenderObject對(duì)象的時(shí)候,如果需要,也會(huì)同時(shí)被創(chuàng)建,當(dāng)然也有可能在執(zhí)行JavaScript代碼時(shí),更新頁(yè)面的樣式從而需要新創(chuàng)建一個(gè)RenderLayer。##一個(gè)例子以上說(shuō)了那么多,一堆堆的類,一個(gè)個(gè)的建立規(guī)則,聽起來(lái)太抽象,不太容易理解,那么一個(gè)具體的Render樹和RenderLayer樹到底是怎么樣的呢?為了可視化理解Render樹和RenderLayer樹,下面給出一個(gè)具體的例子來(lái)加以解釋和說(shuō)明。首先,讓我們來(lái)看一個(gè)簡(jiǎn)單的HTML網(wǎng)頁(yè),源代碼如下所示。上面的代碼結(jié)構(gòu)很簡(jiǎn)單,就是一些HTML的基本元素組成的,例如HTML,HEAD,DIV,A,IMG,TABLE等等,然后包含一個(gè)特別的HTML5元素CANVAS,而且還有一小段JavaScript代碼。照顧到一些沒有相關(guān)背景的讀者,簡(jiǎn)單解釋一下這段代碼的含義。這段代碼是對(duì)CANVAS元素創(chuàng)建一個(gè)WebGL的Context,有了這個(gè)context,就可以在canvas元素上繪制3D的內(nèi)容。這個(gè)類似于OpenGL或者OpenGLES的context,具體canvas和WebGL會(huì)有另外專門的章節(jié)來(lái)介紹。這段HTML源代碼被WebKit解析后會(huì)生成一顆DOM樹。這段代碼的DOM樹主要結(jié)構(gòu)如本章第一幅圖中的‘DOM樹’部分所示。當(dāng)DOM樹生成時(shí),WebKit同時(shí)建立了一顆Render樹,如上所說(shuō),代碼的Render樹被打印成如下圖所示的文本信息。首先,上圖中的“l(fā)ayerat(x,x)”表示不同的RenderLayer節(jié)點(diǎn),下面的所有RenderObject均屬于該RenderLayer。以第一個(gè)layer為例,它對(duì)應(yīng)于DOM中的Document節(jié)點(diǎn)。后面的(0,0)表示該節(jié)點(diǎn)在坐標(biāo)系中的位置,最后的1028x683表示該節(jié)點(diǎn)的大小。它包含的RenderView節(jié)點(diǎn)后面的信息也是同樣的意思。其次,看其中最大的部分-第二個(gè)layer,包含了HTML中的絕大部分元素。這里有三點(diǎn)需要解釋一下:第一,Head元素沒有相應(yīng)的RenderObject,如上面所解釋的,因?yàn)樗皇且粋€(gè)可視的元素。第二,Canvas元素不在其中,而是在第三個(gè)layer中(RenderHTMLCanvas)。但是它仍然是RenderBody節(jié)點(diǎn)的子女。第三,匿名的RenderBlock節(jié)點(diǎn),它包含了RenderText,RenderInline等節(jié)點(diǎn),原因如前所說(shuō)。再次,來(lái)看一下第三個(gè)layer。因?yàn)閺腃anvas創(chuàng)建了一個(gè)WebGL3DContext對(duì)象,這里需要重新生成一個(gè)新的layer。最后,來(lái)說(shuō)明一下三個(gè)layer的創(chuàng)建時(shí)間。第一和第二個(gè)layer在創(chuàng)建DOM樹后,會(huì)觸發(fā)創(chuàng)建;第三個(gè)layer測(cè)試資源加載解析好之后,執(zhí)行后面的JavaScript代碼后所創(chuàng)建?;谏厦娴拿枋觯嘈糯蠹乙呀?jīng)對(duì)Render樹和RenderLayer樹有了一定的了解。現(xiàn)在讓我們回憶一下本章的第一幅圖。該圖其實(shí)是給出了示例的HTML網(wǎng)頁(yè)在WebKit中三種樹的對(duì)應(yīng)關(guān)系(僅選取其中重要的部分),相信現(xiàn)在你已經(jīng)了解它們的含義了。源代碼目錄WebKit/Source/WebCore/rendering
WebKit為支持渲染所涉及的各個(gè)類
WebKit/Source/WebCore/dom/
DOM樹的相關(guān)文件,包括一些基礎(chǔ)類及其接口定義
WebKit/Source/WebCore/html/
為HTML網(wǎng)頁(yè)的元素所定義的相關(guān)的基礎(chǔ)類,它們基于DOM的Node類
參考文獻(xiàn)Googledesigndocuments:/developers/design-documents/gpu-accelerated-compositing-in-chrome渲染主循環(huán)(mainloop)和requestAnimationFrame概述曾經(jīng)寫過(guò)一段JavaScript代碼,因?yàn)樯婕暗叫枰h(huán)調(diào)用某個(gè)函數(shù)來(lái)實(shí)現(xiàn)動(dòng)畫的功能,很自然地,我想到了使用setInterval函數(shù)(或者setTimeout,大家是否有類似經(jīng)歷呢?),然后心滿意足地很快的搞定。結(jié)束后,朋友幫忙閱讀了一下代碼,他提醒我是不是可以考慮使用requestAnimationFrame。之前一直知道這個(gè)函數(shù),也知道一些它的一些優(yōu)點(diǎn),問(wèn)題是為什么呢?本著追究到底的精神,決定還是去閱讀一下WebKit相關(guān)代碼和一些相關(guān)文檔,了解它們背后的故事。好吧,本章我將和大家一起來(lái)學(xué)習(xí)和探討這背后的故事…背景接觸過(guò)JavaScript的讀者應(yīng)該有過(guò)了解或者使用setTimeout或者setInterval的經(jīng)歷,其功能是在每個(gè)時(shí)間間隔之后一次性或者重復(fù)多次執(zhí)行一段JavaScript代碼(稱為回調(diào)函數(shù)),以完成特定的動(dòng)畫要求。但是,這里面有還有些疑問(wèn):時(shí)間間隔應(yīng)該設(shè)置為多少才合適呢?跟屏幕的分辨率有關(guān)系嗎?設(shè)置的時(shí)間間隔會(huì)按照預(yù)想的執(zhí)行嗎?動(dòng)畫會(huì)被平滑地顯示出效果嗎?回調(diào)函數(shù)是復(fù)雜的好還是簡(jiǎn)單的好呢?應(yīng)該如何編寫才能效率高呢?與平臺(tái)和瀏覽器相關(guān)嗎?如何適應(yīng)不同平臺(tái)呢?這對(duì)setTimeout和setInterval來(lái)說(shuō)很重要。如果對(duì)mainloop機(jī)制和渲染機(jī)制有一定了解的讀者來(lái)說(shuō),上面這幾條其實(shí)是非常難做到地,哪怕是較為接近理想的結(jié)果。幸運(yùn)地是,總是有聰明的人來(lái)幫助大家解決難題。對(duì)問(wèn)題提出一個(gè)漂亮解決方案的是mozilla的RobertO’Callahan。他的靈感和依據(jù)來(lái)源于CSS。CSS是知道動(dòng)畫什么時(shí)候發(fā)生,所以能夠較為準(zhǔn)確的知道什么時(shí)候刷新UI。對(duì)于JavaScript來(lái)說(shuō),是不是也可以根據(jù)類似的機(jī)制呢?答案是肯定地。其做法是增加一個(gè)新的方法requestAnimationFrame,該方法告訴瀏覽器JavaScript想發(fā)起一個(gè)動(dòng)畫幀,然后在動(dòng)畫幀繪制之前,需要做一些動(dòng)作,這樣瀏覽器可以根據(jù)需要來(lái)優(yōu)化自己的mainloop機(jī)制和調(diào)用時(shí)間點(diǎn),以達(dá)到較好地平衡效果。好吧,下面來(lái)看看mainloop機(jī)制及其工作原理。渲染mainloop因?yàn)閏hromium是多進(jìn)程的結(jié)構(gòu)(參看Chromium多進(jìn)程架構(gòu)篇),所以,跟一般瀏覽器不一樣的是,Browser進(jìn)程UI用戶界面的mainloop和Renderer進(jìn)程的主線程的mainloop不是同一個(gè),分別位于兩個(gè)不同的進(jìn)程,所以UI和渲染可以互相不影響,聽起來(lái)這好像很不錯(cuò),是的,但是問(wèn)題依然存在,那就是Renderer進(jìn)程的渲染工作和JavaScript的執(zhí)行工作都在其主線程中,由mainloop來(lái)負(fù)責(zé)調(diào)度完成,所以競(jìng)爭(zhēng)依然存在。大致過(guò)程是一個(gè)大的循環(huán)加上一個(gè)事件隊(duì)列,具體的過(guò)程,如下圖所示。當(dāng)隊(duì)列中有事件時(shí),從隊(duì)列中取出第一個(gè)事件,設(shè)置相應(yīng)的狀態(tài)信息,處理該事件及其對(duì)應(yīng)的處理函數(shù),直到該函數(shù)處理完后,才重新檢查隊(duì)列中是否有事件。如果有,繼續(xù)處理;如果沒有,則繼續(xù)等待。這其中可以看出,如果隊(duì)列中事件多的時(shí)候,那么很多事件可能來(lái)不及處理,從而造成比較大的延時(shí),因而事件的平均等待時(shí)間會(huì)比較長(zhǎng)。同時(shí),如果事件的處理函數(shù)需要的時(shí)間很長(zhǎng),就會(huì)造成后面的事件一直在等待,同樣會(huì)增加事件的平均等待時(shí)間。而當(dāng)隊(duì)列比較空閑時(shí)或者事件的處理函數(shù)需要的時(shí)間比較短,則事件的平均等待時(shí)間會(huì)相對(duì)小很多。WebKit和Chromium中的實(shí)現(xiàn)理解了mainloop之后,下面來(lái)看一看setTimeout和setInterval的實(shí)現(xiàn)。來(lái)看一下它們的實(shí)現(xiàn):WebKit中setTimeout和setInterval的實(shí)現(xiàn)機(jī)制是類似的,區(qū)別在于后者是重復(fù)性的,見下圖所示的類圖關(guān)系。WebKit會(huì)為DOM中的每個(gè)setTimeout和setInterval的調(diào)用創(chuàng)建一個(gè)DOMTimer,而后該對(duì)象會(huì)由存儲(chǔ)TLS(threadlocalstorage)中的ThreadTimers負(fù)責(zé)管理,其內(nèi)部其實(shí)是一個(gè)最小堆,每次取timeout時(shí)間最小的,同時(shí),時(shí)間相同的Timer可以合并。當(dāng)Timer超時(shí)后,Chromium清除該Timer對(duì)象,同時(shí)調(diào)用相應(yīng)的回調(diào)函數(shù),回調(diào)函數(shù)通常會(huì)更新頁(yè)面的樣式和布局,這會(huì)觸發(fā)relayout,從而觸發(fā)立即重新繪制一個(gè)新幀。結(jié)合上面的描述,我們大致地總結(jié)setTimeout和setInterval主要不足就是:setTimeout和setInterval從不考慮瀏覽器內(nèi)部發(fā)生了其他什么事,它只要求瀏覽器在某個(gè)時(shí)間之后調(diào)用它的回調(diào)函數(shù),無(wú)論瀏覽器很繁忙或者頁(yè)面被隱藏(雖然某些瀏覽器做了這方面的優(yōu)化,例如chromium);setTimeout和setInterval只要求瀏覽器做什么,而不管瀏覽器能不能做到(例如mainloop有很多事件需要處理),這有點(diǎn)強(qiáng)人所難,而且會(huì)帶來(lái)極大的資源浪費(fèi)。舉個(gè)例子,例如屏幕的刷新率是60HZ,但是設(shè)置的時(shí)間間隔是5ms,其實(shí)對(duì)用戶來(lái)說(shuō)根本看不到這些變化,但是額外需要消耗更多的CPU資源,太不環(huán)保了…setTimeout和setInterval可能是編程風(fēng)格方面的考慮。如果每一幀可能在不同的代碼出需要設(shè)置回調(diào)函數(shù),一個(gè)方法是統(tǒng)一到一個(gè)地方,但是這有點(diǎn)勉為其難,另一個(gè)方法是分別用setInterval設(shè)置它們,這個(gè)方法的問(wèn)題是,瀏覽器可能需要計(jì)算更多次,刷新更多次的屏幕,唉?,F(xiàn)在再來(lái)看看requestAnimationFrame的實(shí)現(xiàn),看看其如何解決這些不足之處的。其原理就是其會(huì)申請(qǐng)繪制下一幀,至于什么時(shí)候不知道,由瀏覽器決定,只需要瀏覽器在繪制下一幀前執(zhí)行其設(shè)置的回調(diào)函數(shù),完成JavaScript對(duì)動(dòng)畫所做的設(shè)置和邏輯即可?;具^(guò)程是這樣的:JavaScript調(diào)用requestAnimationFrame,因而相應(yīng)的webkit和chromium會(huì)調(diào)度一個(gè)需要繪制下一證的事件,該事件會(huì)將requestAnimationFrame的調(diào)用上下文和回調(diào)函數(shù)記錄下來(lái);上面的請(qǐng)求會(huì)觸發(fā)Chromium更新頁(yè)面內(nèi)容的事件,該事件被mainloop調(diào)度處理后,會(huì)檢查是否需要調(diào)用動(dòng)畫的相關(guān)處理,因?yàn)橛袆?dòng)畫需要處理,所以會(huì)依次調(diào)用那些回調(diào)函數(shù),JavaScript引擎會(huì)更新相應(yīng)的CSS屬性或者DOM樹修改;Chromium觸發(fā)重新計(jì)算layout(參看layout章節(jié)),更新自己的Renderer樹(參看webkit渲染基礎(chǔ)章節(jié)),而后繪制,完成一幀的渲染。下圖是一個(gè)上述過(guò)程對(duì)應(yīng)的狀態(tài)轉(zhuǎn)換圖,來(lái)源于chromium的官方網(wǎng)站,看著的確比較饒人,可以先理解一下其中幾個(gè)主要的概念:Floortime:指的是繪制下一幀之前需要等待的事件間隔Invalidation:觸發(fā)重新繪制請(qǐng)求的操作;scheduleAnimation:JavaScript調(diào)用requestAnimationFrame所引起的WebKit內(nèi)部請(qǐng)求調(diào)度動(dòng)畫的操作;這些狀態(tài)的轉(zhuǎn)換倒是說(shuō)明了,requestAnimationFrame可以很好地和Chromium內(nèi)部的繪制過(guò)程結(jié)合,從而達(dá)到比較好的性能。為了實(shí)現(xiàn)更好的性能,chromium中對(duì)requestAnimationFrame有三個(gè)設(shè)計(jì)原則1.當(dāng)頁(yè)面不可見時(shí),其回調(diào)函數(shù)不會(huì)被調(diào)用,這可以減少CPU和GPU的使用率,更環(huán)保嘛;2.其最大調(diào)用頻率不會(huì)超過(guò)60hz,無(wú)論屏幕的刷新率是多少,因而回
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年叉車采購(gòu)項(xiàng)目合同樣本4篇
- 二零二五年度高空作業(yè)安全免責(zé)協(xié)議及高空作業(yè)現(xiàn)場(chǎng)安全檢查合同3篇
- 二零二五版建筑安全文明施工與綠色建筑實(shí)施管理合同3篇
- 2025年度環(huán)保型建筑材料采購(gòu)與施工合同4篇
- 2025年度林木種植與養(yǎng)護(hù)一體化服務(wù)合同范本3篇
- 二零二五版粉煤灰運(yùn)輸信息化服務(wù)合同規(guī)范3篇
- 萬(wàn)科魅力之城2024年購(gòu)房協(xié)議3篇
- 2025年度環(huán)保設(shè)備臨時(shí)維護(hù)與檢修合同4篇
- 鋼板橋梁課程設(shè)計(jì)
- 2025年度金融機(jī)構(gòu)股權(quán)收購(gòu)與風(fēng)險(xiǎn)控制協(xié)議3篇
- GA 1551.5-2019石油石化系統(tǒng)治安反恐防范要求第5部分:運(yùn)輸企業(yè)
- 拘留所教育課件02
- 沖壓生產(chǎn)的品質(zhì)保障
- 《腎臟的結(jié)構(gòu)和功能》課件
- 2023年湖南聯(lián)通校園招聘筆試題庫(kù)及答案解析
- 上海市徐匯區(qū)、金山區(qū)、松江區(qū)2023屆高一上數(shù)學(xué)期末統(tǒng)考試題含解析
- 護(hù)士事業(yè)單位工作人員年度考核登記表
- 天津市新版就業(yè)、勞動(dòng)合同登記名冊(cè)
- 產(chǎn)科操作技術(shù)規(guī)范范本
- 人教版八年級(jí)上冊(cè)地理全冊(cè)單元測(cè)試卷(含期中期末試卷及答案)
- 各種焊工證件比較和釋義
評(píng)論
0/150
提交評(píng)論