Chromium源代碼周邊相關
總體概覽
Chromium分為3個大子產品(包括第三方庫):浏覽器,渲染器,WebKit。浏覽器負責主程序,UI展現和I/O。渲染器一般是由浏覽器調用的标簽子程序。它嵌入WebKit中做布局和渲染。
你應該熟讀多程序架構和Chromium如何顯示web頁。
快速介紹sln檔案
有2個sln檔案,chrome.sln是使用V8引擎的普通版本,chrome_kjs.sln是使用Webkit的JavaScriptCore(JSC)引擎的版本。一般都是用V8,保留JSC就是為了看有bug時,這bug是不是跟V8有關。
-App/chrome_dll和App/chrome工程是啟動代碼。
-Libraries/base工程是通用庫代碼。這個代碼是所有工程共用的,我們會盡量讓它保持短小精悍。
-Browser/common中是浏覽器相關的通用庫代碼。它是浏覽器和渲染器共用的。
-Webkit(隻讀)目錄下的工程是Webkit的代碼。它的上層是Google的支援Windows接口的WebKit(自己的)/port,WebKit(自己的)/glue是嵌入層。
-glue與Browser/renderer工程相關,它用來展示每個子标簽頁裡跑的子程序。
-Browser/browser工程提供了UI、存儲、網絡等功能。
頂層工程
拿到Chromium代碼後,你會發現有很多頂級目錄。有這些:
-base:所有子工程共用的通用代碼。它包括字元串操作,通用庫等。隻有在所有頂層工程間共用時,才往這裡加東西。
-gfx:共用圖形類。它是Chromium的GUI的基礎。chrome/common/gfx下還有一些圖形類,它是Chromium應用程式專用的。
-breakpad:Google的開源crash報告工程。它是直接從Google Code的svn中拉過來的。
-build:所有工程共用的編譯配置。
-chrome:Chromium浏覽器(參考下面)。
-data:運作證書測試的data檔案。
-gears:離線浏覽。
-googleurl:Google的開源URL解析判别庫。它是直接從Google Code的svn中拉過來的。
-net:Chromium中的網絡庫。在運作webkit裡的簡單test_shell時,它可以從Chromium中分離出來單獨用。可以參考下chrome/common/net。
-sandbox:沙箱。它能防止惡意頁面修改你的系統。
-skia:Google為Android開發的Skia圖形庫。它是從Android代碼裡複制過來的。base/gfx中的附加類封裝了它。
-testing:包含Google的開源單元測試工具GTest。
-third_party:集中放第三方庫,比如圖檔解碼,壓縮庫之類的。也有些Chrome專用的第三方庫放在chrome/third_party裡。添加新包。
-tools:不解釋,你知道的。
-V8:V8 js庫。它是直接從Google Code的svn中拉過來的。
-webkit:所有跟Chromium的Webkit相關的東西:
-build:工程檔案和其他工程相關的配置。
-data:大多數目錄下的data都是給轉換層的單元測試用的。layout_tests目錄是直接從Apple拉過來的WebKit的布局測試套件。
-glue:glue層是個嵌入層。它負責Webcore類型和Chrome程式類型(大多STL)之間的轉換,同時還提供了很多好用的函數來通路我們需要用到的Webcore的對象。
-tools
-layout_tests:運作WebCore布局測試的腳本。
-merge:幫助合并WebKit樹的腳本。
-npapi_layout_test_plugin:專門用來測試插件層的插件。
-test_shell:一個非常簡單的獨立的浏覽器。不用運作巨大的Chromium程式,就可以測試glue和轉換代碼。
"chrome/"下目錄樹的簡要說明
-app:"app"是最基礎的程式。它一開始就運作,根據目前程序的能力來分别分派到浏覽器或渲染器代碼中。它包括chrome.exe和chrome.dll 2個工程。一般你不需要修改它,頂多就是改改圖檔和字元串這些資源。
-locales:此工程用來生成本地化dll。
-resources:圖示和光标。
-theme:窗體主題的圖檔。
-browser:前端包括主窗體和UI,後端包括I/O和存儲。它與渲染器管理頁面相關。
-common:這下面的檔案在浏覽器和渲染器之間共用,同時也給一些其他的工程用。這些代碼是專門給Chromium(而不是基礎程式)用的,但在chrome/浏覽器和chrome/渲染器目錄之間可以共用。
-gfx:Chromium專用的圖形和渲染相關的助手代碼。有些圖形上的東西必須也提供給外面頂層的base/gfx使用。
-net:net上層的頂級子產品上一些Chromium專用的東西。将會與browser/net合并。
-installer:制作安裝包(MSI包)的源檔案和工程檔案。
-plugin:在其他程序中運作浏覽器插件的代碼。
test:
-automation:測試用來驅動浏覽器UI的,比如test/ui,test/startup等。它會在浏覽器中與browser/automation互動。
-page_cycler:執行頁面循環測試的代碼(比如性能測試)。參考tools/perf/dashboard。
-reliability:頁面載入的分布式可靠性測試和crash定位。
-selenium:ajax和js等的第三方測試套件的選擇性測試的代碼。參考test/third_party/selenium_core。
-startup:測試啟動性能。參考tools/perf/dashboard和tools/test/reference_build。
-ui:浏覽器UI測試,比如打開标簽頁等。它使用test/automation來完成大部分操作。
-unit:單元測試的基礎代碼。測試代碼一般和被測試檔案放在一起,命名為*_unittest.cc。
-third_party:Chromium專用的第三方庫。還有一些第三方庫放在頂層的third_party裡。
-tools
-build:編譯相關的工具等。
-buildbot:編譯機器人配置。編譯機器人管理自動編譯系統。參考third_pary/buildbot。
-win:在Windows下編譯的東西,包括工程屬性用的.vsprops檔案和腳本。
-memory:記憶體工具。現在包括設定頁面堆配置的gflags。
-perf/dashboard:将性能日志(比如test/startup_test)轉換為資料和圖形。
-profiles:曆史資料随機生成器。用來生成測試概況(profile)。
-views:用來開發UI的簡單架構,提供渲染、布局和事件處理。大多數浏覽器UI都是用這個系統實作的。這個目錄包括基礎對象。另外還有些浏覽器專用的對象在browser/views裡。
通用操作的代碼路徑
附加資訊和更多的例子在Chromium如何顯示web頁面裡。
程式啟動
1.WinMain函數在chrome/app/main.cc中,它在chrome工程中編譯。
2.WinMain執行Google自動更新用戶端。它會找到目前版本的子目錄,然後載入chrome.dll。
3.調用剛載入的庫裡的ChromeMain,代碼在chrome_dll工程裡的chrome_main.cc。
4.ChromeMain初始化通用元件,然後如果指令行指定這是個子程序,就調用chrome/renderer/renderer_main.cc裡的RendererMain,如果不是程式的新拷貝(也就是說程式第一次啟動),就調用chrome/browser/browser_main.cc裡的BBrowserMain。現在已經啟動了,是時候啟動浏覽器了。
5.BrowserMain會初始化浏覽器。不同的情況它會以不同的方式運作,比如如果是安裝web應用程式就安裝它,如果是在測試就連接配接到自動系統,等等。
6.chrome/browser/browser.cc裡會建立一個新的浏覽器對象,它會調用browser_init.cc裡的LaunchWithProfile。這個對象會生成一個頂級視窗。這時會生成第一個标簽頁。
啟動标簽頁并初始化導航
1.chrome/browser/browser.cc裡的Browser::AppendTab會被調用,并建立一個新标簽頁。
2.建立一個browser/tab_contents/tab_contents.cc裡的TabContents對象。
3.TabContents會通過chrome/browser/tab_contents/render_view_host_manager.cc裡的RenderViewMostManager的初始化函數建立一個chrome/browser/renderer_host/render_view_host.cc裡的RenderViewHost。通過SiteInstance,RenderViewHost會生成一個新的渲染程序(RenderProcess),或者重用一個已有的。RenderProcess是浏覽器裡的一個對象,負責一個單獨的渲染子程序。
4.chrome/browser/tab_contents/navigation_controller.cc裡的NavigationController會被标簽頁容器持有,并調用NavigationController::LoadURL來在新标簽頁打開一個URL位址。從這裡到後面的3個步驟,描述的是從位址欄打開一個URL位址。
從位址欄打開一個URL位址
1.當使用者輸入,或者位址欄收到一個回車時,自動完成編輯框會算出最終目标URL,并傳給AutocompleteEdit::OpenURL。(說使用者輸入可能不夠确切——比如,搜尋結果裡的一個URL。)
2.NavigationController::LoadURL裡的導航控制器(NavigationController)會去打開URL。
3.NavigationController調用TabContents::Navigate,并傳入一個NavigationEntry來負責頁面轉換。這将會導緻子程序如果需要,就建立一個RenderViewHost。如果是第一次導航,就不會有RenderView,否則如果渲染器crash了,會導緻它被crash裡的覆寫。
4.Navigate繼續調用RenderViewHost::NavigateToEntry。NavigationController存儲這個導航入口,但是會打一個"pending"的标記,因為它不知道到底會不會轉換(可能主機無法解析)。
5.RenderViewHost::NavigateToEntry會給渲染程序裡新的RenderView發一個ViewMsg_Navigate。
6.導航時,RenderView可能會導航,可能會失敗,也可能會導航到其他地方(比如使用者點了一個連結)。RenderViewHost會等待RenderView發一個ViewHostMsg_FrameNavigate。
7.當載入狀态被WebKit改成"committed"時(server響應并發送了資料),RenderView就會發這個消息,并被RenderViewHost::OnMsgNavigate捕獲處理。
8.過程中NavigationEntry會更新載入過程中的資訊。例如點選的連結,之前浏覽器是不知道的。如果導航是浏覽器初始化的,就像啟動時那樣,在導航過程中可能會由于重定向而改變URL位址。
9.NavigationController給帳戶把新的資訊更新到導航清單中。
導航和會話曆史
每個NavigationEntry都會存儲一個頁面ID和曆史狀态資料。頁面ID用來唯一辨別一個頁面載入,這樣我們就知道哪一個NavigationEntry與之對應。它在頁面被送出時配置設定,是以一個pending狀态的NavigationEntry的頁面ID是-1。曆史狀态資料就是WebCore::HistoryItem序列化的一個字元串。它裡面包含了頁面URL,子frame URL,以及表格資料。
1.當浏覽器初始化請求時(在位址欄中輸入,或者點選後退/前進/重新整理)
1.生成一個WebRequest來表示導航,它會一直為書記員(bookkeeping)帶着頁面ID之類的資訊。新導航的ID是-1。當頁面是第一次通路時,舊入口的導航會把ID配置設定給NavigationEntry。到後面載入送出時,還會查詢這個資訊。
2.主WebFrame會被調用來載入新的請求。
2.渲染器初始化請求時(使用者點選連結,js改變了location,等):
1.WebCore::FrameLoader的各個載入方法會被調用來載入請求。
3.無論什麼情況,當收到伺服器傳回的第一個包時,載入會被送出(不再是"pending"或"provisional"狀态)。
4.如果有一個新的導航,會fork出一個新的WebCore,它會建立一個新的HistoryItem,并加到BackForwardList中。用這個方法,我們可以區分哪個導航是新的,哪個是會話曆史導航。
5.RenderView::DidCommitLoadForFrame處理載入的送出。在這,前一個頁面的狀态通過ViewHostMsg_UpdateState消息被存儲在會話曆史中。這告訴浏覽器去更新對應的NavigationEntry(通過RenderView的目前頁面ID辨別)為新的曆史狀态。
6.RenderView的目前頁面ID被更新為送出頁面的ID。對于新的導航,會生成一個新的唯一頁面ID。對會話曆史導航,頁面ID是第一次通路時的ID,初始化導航時,已經把它存在WebRequest裡了。
7.發送一個ViewHostMsg_FrameNavigate消息給浏覽器,更新對應的NavigationEntry(由RenderView剛更新的頁面ID辨別)的新URL和其他資訊。