chrome多進程架構

問題

構建幾乎不會崩潰或掛起的渲染引擎幾乎是不可能的。構建完全安全的渲染引擎幾乎也是不可能的。

在某種程度上,2006年左右的網絡瀏覽器的狀態類似于過去的單用戶,協作多任務操作系統。由于此類操作系統中的行為異常的應用程序可能會破壞整個系統,因此Web瀏覽器中的行為異常的網頁也可能因此而崩潰。它只需要一個瀏覽器或插件錯誤即可關閉整個瀏覽器和所有當前運行的選項卡。

現代操作系統更強大,因為它們將應用程序置于相互隔離的單獨進程中。一個應用程序中的崩潰通常不會損害其他應用程序或操作系統的完整性,并且每個用戶對其他用戶數據的訪問受到限制。

建筑概述

我們對瀏覽器選項卡使用單獨的過程,以保護整個應用程序免受渲染引擎中的錯誤和干擾。我們還限制了每個渲染引擎進程對其他進程以及系統其余部分的訪問。在某些方面,這為Web瀏覽帶來了內存保護和訪問控制帶給操作系統的好處。

我們將運行UI并管理選項卡和插件過程的主要過程稱為“瀏覽器過程”或“瀏覽器”。同樣,特定于選項卡的過程稱為“渲染過程”或“渲染器”。渲染器使用Blink開源布局引擎來解釋和布局HTML。

管理渲染過程

每個渲染進程都有一個全局RenderProcess對象,該對象管理與父瀏覽器進程的通信并維護全局狀態。瀏覽器RenderProcessHost?為每個渲染進程維護一個對應項,該進程管理瀏覽器狀態和渲染器的通信。瀏覽器和渲染器使用Chromium的IPC系統進行通信。

管理視圖

每個渲染過程都有一個或多個RenderView對象,由管理RenderProcess,這些對象對應于內容的選項卡。對應項?在渲染器中RenderProcessHost維護RenderViewHost與每個視圖對應的內容。每個視圖都有一個視圖ID,該ID用于區分同一渲染器中的多個視圖。這些ID在一個渲染器中是唯一的,但在瀏覽器中不是唯一的,因此要識別視圖,需要一個RenderProcessHostID和一個View ID。通過這些RenderViewHost對象可以完成從瀏覽器到特定內容選項卡的通信,這些對象知道如何將消息通過它們發送RenderProcessHostRenderProcessRenderView。

組件和接口

在渲染過程中:

  • 在瀏覽器中RenderProcess使用相應的句柄處理IPC?RenderProcessHost。RenderProcess每個渲染過程只有一個對象。這就是所有瀏覽器↔渲染器通信的發生方式。
  • RenderView對象RenderViewHost在瀏覽器進程中(通過RenderProcess)和我們的WebKit嵌入層與其對應的對象通信。此對象表示選項卡或彈出窗口中一個網頁的內容

在瀏覽器過程中:

  • Browser對象表示頂級瀏覽器窗口。
  • RenderProcessHost對象表示單個瀏覽器↔渲染器IPC連接的瀏覽器端。有一個RenderProcessHost在每個渲染過程中的瀏覽器進程。
  • RenderViewHost對象封裝的通信與遠程RenderViewRenderWidgetHost處理用于輸入和繪畫RenderWidget在瀏覽器中。

有關此嵌入的工作方式的更多詳細信息,請參閱Chromium如何顯示網頁??設計文檔。

共享渲染過程

通常,每個新窗口或選項卡都會在新過程中打開。瀏覽器將產生一個新進程,并指示它創建一個RenderView。

有時有必要或希望在選項卡或窗口之間共享渲染過程。Web應用程序會打開一個新窗口,希望與之進行同步通信,例如,使用??JavaScript中的window.open。在這種情況下,當我們創建新的窗口或選項卡時,我們需要重用打開窗口的過程。如果進程總數太大,或者用戶已經打開一個導航到該域的進程,我們還可以為新進程分配新選項卡的策略。這些策略在過程模型中進行了描述。

檢測崩潰或行為異常的渲染器

與瀏覽器進程的每個IPC連接都會監視進程句柄。如果發出了這些句柄的信號,則表示渲染過程已崩潰,并且向選項卡通知了崩潰?,F在,我們顯示一個“悲傷的標簽”屏幕,通知用戶渲染器崩潰了??梢酝ㄟ^按下重新加載按鈕或開始新的導航來重新加載頁面。發生這種情況時,我們注意到沒有任何流程,而是創建一個新的流程。

沙盒渲染器

鑒于渲染器在單獨的進程中運行,我們有機會通過sandboxing限制其對系統資源的訪問。例如,我們可以確保渲染器對網絡的唯一訪問是通過其父瀏覽器進程進行的。同樣,我們可以使用主機操作系統的內置權限來限制其對文件系統的訪問。

除了限制渲染器對文件系統和網絡的訪問之外,我們還可以限制對渲染器對用戶的顯示和相關對象的訪問。我們在用戶看不見的單獨Windows“?桌面?”?上運行每個渲染過程。這樣可以防止受損的渲染器打開新窗口或捕獲擊鍵。

回饋記憶

給定渲染器在單獨的進程中運行,將隱藏的選項卡視為較低優先級就變得很簡單。通常,Windows上最小化的進程會將其內存自動放入“可用內存”池中。在低內存情況下,Windows將在交換出更高優先級的內存之前將該內存交換到磁盤上,從而有助于使用戶可見的程序具有更高的響應速度。我們可以將相同的原理應用于隱藏的標簽。當渲染進程沒有頂級選項卡時,我們可以釋放該進程的“工作集”大小,以提示系統在必要時先將該內存換出到磁盤。因為我們發現減小工作集大小也會降低用戶在兩個選項卡之間切換時的選項卡切換性能,所以我們逐漸釋放此內存。這意味著,如果用戶切換回最近使用的選項卡,則與最近使用的選項卡相比,該選項卡的內存更有可能被分頁。具有足夠內存來運行所有程序的用戶根本不會注意到此過程:Windows僅會在需要時才真正回收這些數據,因此,如果有足夠的內存,則不會對性能造成任何影響。

這有助于我們在低內存情況下獲得更好的內存占用。與很少使用的背景標簽相關聯的內存可以完全換出,而前景標簽的數據可以完全加載到內存中。相比之下,單進程瀏覽器會將所有選項卡的數據隨機分布在其內存中,并且不可能如此干凈地分離使用和未使用的數據,從而浪費內存和性能。

插件和擴展

Firefox風格的NPAPI插件在其自身的進程中運行,與渲染器分開。插件體系結構中對此進行了詳細描述。?

“?網站隔離”項目旨在提供渲染器之間的更多隔離,該項目的早期交付成果包括在隔離的進程中運行Chrome的HTML / JavaScript內容擴展。

chrome渲染引擎blink如何工作

眨眼的工作原理

bit.ly/how-blink-works

作者:haraken @

最后更新:2018年8月14日

狀態:PUBLIC

 

在眨眼上工作并不容易。對于新的Blink開發人員而言,這并不容易,因為已經引入了許多特定于Blink的概念和編碼約定來實現非??焖俚匿秩疽?。即使對于有經驗的Blink開發人員來說,這也不容易,因為Blink龐大且對性能,內存和安全性極為敏感。

 

本文檔旨在提供1?萬英尺的“ Blink的工作原理”概述,我希望它將有助于Blink開發人員快速熟悉該體系結構:

 

  • 該文檔不是有關Blink詳細架構和編碼規則(可能會更改和過時)的詳盡教程。相反,該文檔簡明扼要地描述了Blink的基本原理(短期內不太可能改變),并指出了您想了解更多信息時可以閱讀的資源。
  • 該文檔未解釋特定功能(例如ServiceWorkers,編輯)。而是該文檔解釋了廣泛的代碼庫所使用的基本功能(例如,內存管理,V8 API)。

 

有關Blink開發的更多常規信息,請參閱Chromium Wiki頁面。

 

眨眼做什么

流程/線程架構

工藝流程

線程數

閃爍的初始化

目錄結構

內容公共API和Blink公共API

目錄結構和依賴性

WTF

內存管理

任務調度

頁面,框架,文檔,DOMWindow等

概念

OOPIF

分離的框架/文件

Web IDL綁定

V8和閃爍

隔離,上下文,世界

V8 API

V8包裝器

渲染管線

有什么問題嗎

 

眨眼做什么

Blink是Web平臺的渲染引擎。粗略地說,Blink實現了所有在瀏覽器選項卡中呈現內容的內容:

 

  • 實施Web平臺的規范(例如HTML標準),包括DOM,CSS和Web IDL
  • 嵌入V8并運行JavaScript
  • 從基礎網絡堆棧請求資源
  • 建立DOM樹
  • 計算樣式和布局
  • 嵌入Chrome合成器并繪制圖形

 

許多用戶(例如Chromium,Android WebView和Opera)通過內容公開API嵌入了Blink?。

 

從代碼庫的角度來看,“閃爍”通常是指// third_party / blink /。從項目角度來看,“閃爍”通常表示實現Web平臺功能的項目。實現Web平臺功能的代碼跨越// third_party / blink /,// content / renderer /,// content /瀏覽器/和其他地方。

流程/線程架構

工藝流程

鉻具有多工藝體系結構。Chromium具有一個瀏覽器進程和N個沙盒渲染器進程。閃爍在渲染器進程中運行。

 

創建了多少個渲染器進程?出于安全原因,隔離跨站點文檔之間的內存地址區域非常重要(這稱為Site Isolation)。從概念上講,每個渲染器過程最多應專用于一個站點。但是實際上,當用戶打開太多標簽頁或設備沒有足夠的RAM時,有時將每個渲染器進程限制在一個站點上有時會很繁瑣。然后,渲染器進程可以由從不同站點加載的多個iframe或標簽共享。這意味著一個選項卡中的iframe可以由不同的渲染器進程托管,而不同選項卡中的iframe可以由相同的渲染器進程托管。渲染器進程,iframe和Tab之間沒有1:1映射。

 

假定渲染器進程在沙箱中運行,則Blink需要要求瀏覽器進程調度系統調用(例如,文件訪問,播放音頻)并訪問用戶配置文件數據(例如Cookie,密碼)。這種瀏覽器-渲染器過程通信是由Mojo實現的。(注意:過去我們使用的是Chromium IPC,但仍然有很多地方在使用它。但是,它已被棄用,并在后臺使用Mojo。)在Chromium方面,服務化正在進行中,并將瀏覽器過程抽象為一組“服務。從Blink角度來看,Blink可以僅使用Mojo與服務和瀏覽器進程進行交互。

 

如果您想了解更多信息:

 

  • 多進程架構
  • Blink中的Mojo編程:platform / mojo / MojoProgrammingInBlink.md

線程數

在渲染器進程中創建了多少個線程?

 

眨眼有一個主線程,N個工作線程和幾個內部線程。

 

幾乎所有重要的事情都在主線程上發生。所有JavaScript(工人除外),DOM,CSS,樣式和布局計算都在主線程上運行。假設主要是單線程體系結構,Blink進行了高度優化以最大化主線程的性能。

 

眨眼可能會創建多個工作線程來運行Web Workers,ServiceWorkerWorklets。

 

Blink和V8可能會創建幾個內部線程來處理webaudio,數據庫,GC等。

 

對于跨線程通信,必須使用通過PostTask API傳遞消息。不建議使用共享內存編程,除了出于性能原因確實需要使用它的幾個地方。這就是為什么您在Blink代碼庫中看不到很多MutexLocks的原因。

 

如果您想了解更多信息:

 

閃爍的初始化和完成

眨眼由BlinkInitializer :: Initialize()初始化。在執行任何Blink代碼之前必須調用此方法。

 

另一方面,Blink從未完成。也就是說,渲染器進程被強制退出而不進行清理。原因之一是性能。另一個原因是,通常很難以一種有序的方式清理渲染器進程中的所有內容(這是不值得的工作)。

目錄結構

內容公共API和Blink公共API

內容公共API是使嵌入程序嵌入呈現引擎的API層。內容公共API必須小心維護,因為它們會暴露在嵌入程序中。

 

眨眼公共API是將// // third_party / blink /的功能公開給Chromium的API層。該API層只是從WebKit繼承的歷史工件。在WebKit時代,Chromium和Safari共享WebKit的實現,因此需要API層才能將WebKit的功能公開給Chromium和Safari。既然Chromium是// third_party / blink /的唯一嵌入者,那么API層就沒有意義了。通過將網絡平臺代碼從Chromium移到Blink(該項目稱為Onion Soup),我們正在積極減少Blink公共API的數量。

 

目錄結構和依賴性

// third_party / blink /具有以下目錄。有關這些目錄的更詳細定義,請參閱此文檔

 

  • 平臺/
    • Blink的較低級功能的集合,這些功能是從整體內核中剔除的。例如,幾何和圖形工具。
  • 核心/和模塊/
    • 規范中定義的所有Web平臺功能的實現。core /實現與DOM緊密結合的功能。模塊/實現更多獨立功能。例如webaudio,indexeddb。
  • 綁定/核心/和綁定/模塊/
    • 從概念上講,bindings / core /是core /的一部分,而bindings / modules /是modules /的一部分。大量使用V8 API的文件被放在bindings / {core,modules}中。
  • 控制器/
    • 一組使用core /和modules /的高級庫。例如devtools前端。

 

依存關系按以下順序流動:

 

  • 鉻=>控制器/ =>模塊/和綁定/模塊/ =>核心/和綁定/核心/ =>平臺/ =>底層基元,例如// base,// v8和// cc

 

Blink仔細維護暴露于// third_party / blink /的低級基元列表。

 

如果您想了解更多信息:

 

WTF

WTF是一個“特定于眨眼的基礎”庫,位于platform / wtf /。我們正在嘗試盡可能多地統一Chromium和Blink之間的編碼原語,因此WTF應該很小。需要此庫是因為確實需要針對Blink的工作量和Oilpan(Blink GC)優化許多類型,容器和宏。如果類型是在WTF中定義的,則Blink必須使用WTF類型而不是// base或std庫中定義的類型。最受歡迎的是矢量,哈希集,哈希圖和字符串。眨眼應該使用WTF :: Vector,WTF :: HashSet,WTF :: HashMap,WTF :: String和WTF :: AtomicString而不是std :: vector,std :: * set,std :: * map和std :: string 。

 

如果您想了解更多信息:

 

內存管理

就Blink而言,您需要關心三個內存分配器:

 

 

您可以使用USING_FAST_MALLOC()在PartitionAlloc的堆上分配一個對象:

 

類SomeObject {

??USING_FAST_MALLOC(SomeObject);

??靜態std :: unique_ptr <SomeObject> Create(){

????返回std :: make_unique <SomeObject>();?//分配在PartitionAlloc的堆上。

}

};

 

由PartitionAlloc分配的對象的生存期應由scoped_refptr <>或std :: unique_ptr <>管理。強烈建議不要手動管理生命周期。閃爍禁止手動刪除。

 

您可以使用GarbageCollected在Oilpan的堆上分配一個對象:

 

類SomeObject:公共GarbageCollected <SomeObject> {

??靜態SomeObject * Create(){

????返回新的SomeObject;?//分配在Oilpan的堆上。

??}

};

 

Oilpan分配的對象的生存期由垃圾收集自動管理。您必須使用特殊的指針(例如Member <>,Persistent <>)將對象保存在Oilpan的堆上。請參閱此API參考以熟悉有關Oilpan的編程限制。最重要的限制是不允許您在油鍋對象的析構函數中觸摸任何其他油鍋對象(因為無法保證銷毀順序)。

 

如果您既不使用USING_FAST_MALLOC()也不使用GarbageCollected,則在系統malloc的堆上分配對象。在眨眼中強烈建議不要這樣做。所有Blink對象應由PartitionAlloc或Oilpan分配,如下所示:

 

  • 默認情況下使用Oilpan。
  • 僅在以下情況下才使用PartitionAlloc:1)對象的生存期非常明確并且std :: unique_ptr <>或scoped_refptr <>足夠,2)在Oilpan上分配對象會帶來很多復雜性,或者3)在Oilpan上分配對象會導致給垃圾收集運行時帶來了不必要的壓力。

 

無論使用PartitionAlloc還是Oilpan,都必須非常小心,不要創建懸空的指針(注意:強烈建議不要使用原始指針)或內存泄漏。

 

如果您想了解更多信息:

 

任務調度

為了提高渲染引擎的響應速度,Blink中的任務應盡可能異步執行。不鼓勵同步IPC / Mojo和任何其他可能花費幾毫秒的操作(盡管某些操作是不可避免的,例如用戶的JavaScript執行)。

 

呈現器進程中的所有任務都應使用正確的任務類型發布到Blink Scheduler,如下所示:

 

//使用kNetworking的任務類型將任務發布到框架的調度程序

frame-> GetTaskRunner(TaskType :: kNetworking)-> PostTask(…,WTF :: Bind(&Function));

 

Blink Scheduler維護多個任務隊列,并巧妙地對任務進行優先級排序,以最大化用戶感知的性能。重要的是要指定適當的任務類型,以使Blink Scheduler能夠正確,智能地調度任務。

 

如果您想了解更多信息:

 

  • 如何發布任務:platform / scheduler / PostTask.md

頁面,框架,文檔,DOMWindow等

概念

頁面,框架,文檔,ExecutionContext和DOMWindow是以下概念:

 

  • 頁面與選項卡的概念相對應(如果未啟用下面說明的OOPIF)。每個渲染器進程可能包含多個選項卡。
  • 框架對應于框架(主框架或iframe)的概念。每個頁面可以包含一個或多個以樹狀層次結構排列的框架。
  • DOMWindow對應于JavaScript中的窗口對象。每個框架都有一個DOMWindow。
  • Document對應于JavaScript中的window.document?對象。每個框架都有一個文檔。
  • ExecutionContext是一個抽象文檔(用于主線程)和WorkerGlobalScope(用于工作線程)的概念。

 

渲染過程:頁面= 1:N

 

頁:框架= 1:M.

 

框架:DOMWindow:文檔(或ExecutionContext)= 1:1:1在任何時間點,但映射可能隨時間而變化。例如,考慮以下代碼:

 

iframe.contentWindow.location.href =“ https://example.com”;

 

在這種情況下,將為https://example.com創建一個新的DOMWindow和一個新的Document?。但是,可以重復使用該框架。

 

(注意:確切地說,在某些情況下會創建一個新的Document,但是DOMWindow和Frame會被重用。甚至還有一些更復雜的情況。)

 

如果您想了解更多信息:

 

  • 核心/框架/FrameLifecycle.md

進程外iframe(OOPIF)

站點隔離使事情變得更加安全,但更加復雜。🙂站點隔離的想法是為每個站點創建一個渲染器進程。(網站是頁面的可注冊域+ 1標簽及其URL方案。例如,https://mail.example.comhttps://chat.example.com在同一網站中,但https:// noodles.comhttps://pumpkins.com都沒有。)如果一個頁面包含一個跨站點IFRAME,該頁面可以由兩個渲染過程托管??紤]以下頁面:

 

<!– https://example.com –>

<身體>

<iframe src =” https://example2.com”> </ iframe>

</ body>

 

主框架和<iframe>可以由不同的渲染器進程托管。渲染器進程本地的幀由LocalFrame表示,而不是渲染器進程本地的幀由RemoteFrame表示。

 

從主框架的角度來看,主框架是LocalFrame,而<iframe>是RemoteFrame。從<iframe>的角度來看,主框架是RemoteFrame,而<iframe>是LocalFrame。

 

本地框架和遠程框架(可能存在于不同的渲染器進程中)之間的通信是通過瀏覽器進程進行處理的。

 

如果您想了解更多信息:

 

分離的框架/文件

相框/文檔可能處于分離狀態??紤]以下情況:

 

doc = iframe.contentDocument;

iframe.remove();?//將iframe與DOM樹分離。

doc.createElement(“ div”);?//但是您仍然可以在分離的框架上運行腳本。

 

棘手的事實是,您仍然可以在分離的框架上運行腳本或DOM操作。由于框架已經分離,大多數DOM操作將失敗并引發錯誤。不幸的是,分離框架上的行為在瀏覽器之間并不能真正實現互操作,在規范中也沒有明確定義?;旧?,人們期望JavaScript可以繼續運行,但是大多數DOM操作應該會因某些適當的異常而失敗,例如:

 

無效someDOMOperation(…){

??if(!script_state _-> ContextIsValid()){//框架已經分離

????…;//設置例外等

????返回;

}

}

 

這意味著在通常情況下,當框架分離時,Blink需要執行一系列清理操作。您可以通過從ContextLifecycleObserver繼承來做到這一點,如下所示:

 

類SomeObject:公共GarbageCollected <SomeObject>,公共ContextLifecycleObserver {

??void ContextDestroyed()覆蓋{

????//在此進行清理操作。

}

???SomeObject(){

????//在這里進行清理操作不是一個好主意,因為現在進行清理已經太遲了。此外,不允許析構函數接觸Oilpan堆上的任何其他對象。

??}

};

Web IDL綁定

當JavaScript訪問node.firstChild時,將調用node.h?中的Node :: firstChild()。它是如何工作的?讓我們看一下node.firstChild的工作方式。

 

首先,您需要根據規范定義一個IDL文件:

 

// node.idl

接口Node:EventTarget {

??[…]只讀屬性Node?第一個孩子;

};

 

Web IDL的語法在Web IDL規范中定義。[…]?稱為IDL擴展屬性。一些IDL擴展屬性是在Web IDL規范中定義的,而另一些是特定于Blink的IDL擴展屬性。除了特定于閃爍的IDL擴展屬性外,IDL文件應以符合規范的方式編寫(即,僅從規范中復制并粘貼)。

 

其次,您需要為Node定義一個C ++類,并為firstChild實現一個C ++ getter:

 

class EventTarget:public Sc??riptWrappable {//所有暴露給JavaScript的類都必須從ScriptWrappable繼承。

…;

};

 

類Node:public EventTarget {

??DEFINE_WRAPPERTYPEINFO();?//所有具有IDL文件的類都必須具有此宏。

??節點* firstChild()const {return first_child_;?}

};

 

在通常情況下,就是這樣。生成node.idl時,IDL編譯器會自動為Node接口和Node.firstChild生成Blink-V8綁定。自動生成的綁定是在//src/out/{Debug,Release}/gen/third_party/blink/renderer/bindings/core/v8/v8_node.h中生成的。當JavaScript調用node.firstChild時,V8會調用v8_node.h中的V8Node :: firstChildAttributeGetterCallback(),然后會調用您在上面定義的Node :: firstChild()。

 

如果您想了解更多信息:

 

V8和閃爍

隔離,上下文,世界

當您編寫涉及V8 API的代碼時,了解隔離,上下文和世界的概念很重要。它們分別在代碼庫中由v8 :: Isolate,v8 :: Context和DOMWrapperWorld表示。

 

隔離對應于物理線程。隔離:閃爍中的物理線程= 1:1。主線程具有自己的隔離。輔助線程具有其自己的隔離。

 

上下文對應于全局對象(在使用框架的情況下,它是框架的窗口對象)。由于每個框架都有其自己的窗口對象,因此渲染器進程中存在多個上下文。調用V8 API時,必須確保您使用的是正確的上下文。否則,v8 :: Isolate :: GetCurrentContext()將返回錯誤的上下文,在最壞的情況下,它將最終導致對象泄漏并導致安全問題。

 

World是支持Chrome擴展程序的內容腳本的概念。世界與Web標準中的任何內容都不對應。內容腳本希望與網頁共享DOM,但是出于安全原因,必須將內容腳本的JavaScript對象與網頁的JavaScript堆隔離。(還必須將一個內容腳本的JavaScript堆與另一個內容腳本的JavaScript堆隔離。)為了實現隔離,主線程為網頁創建了一個主世界,為每個內容腳本創建了一個隔離世界。主世界和孤立世界可以訪問相同的C ++ DOM對象,但是它們的JavaScript對象是孤立的。通過為一個C ++ DOM對象創建多個V8包裝器來實現這種隔離。也就是說,每個世界一個V8包裝器。

 

上下文,世界和框架之間有什么關系?

 

想象一下,在主線程上有N個世界(一個主世界+(N – 1)個孤立世界)。然后,一個框架應具有N個窗口對象,每個窗口對象用于一個世界。上下文是與窗口對象相對應的概念。這意味著,當我們有M個框架和N個世界時,我們有M * N個上下文(但是這些上下文是惰性創建的)。

 

如果是工人,則只有一個世界和一個全局對象。因此,只有一個上下文。

 

同樣,當您使用V8 API時,應該非常小心使用正確的上下文。否則,您最終將在孤立的世界之間泄漏JavaScript對象并造成安全災難(例如,來自A.com的擴展程序可以操縱來自B.com的擴展程序)。

 

如果您想了解更多信息:

 

V8 API

//v8/include/v8.h中定義了許多V8 API?。由于V8 API是低級的并且難以正確使用,因此platform / bindings /提供了一堆包裝V8 API的幫助程序類。您應該考慮盡可能使用助手類。如果您的代碼必須大量使用V8 API,則應將文件放在bindings / {core,modules}中。

 

V8使用句柄指向V8對象。最常見的句柄是v8 :: Local <>,用于從計算機堆棧指向V8對象。在計算機堆棧上分配v8 :: HandleScope之后,必須使用v8 :: Local <>。v8 :: Local <>不應在機器堆棧之外使用:

 

void function(){

??v8 :: HandleScope范圍;

??v8 :: Local <v8 :: Object> object =…;?// 這是對的。

}

 

類SomeObject:公共GarbageCollected <SomeObject> {

??v8 :: Local <v8 :: Object> object_;?//這是錯誤的。

};

 

如果要從計算機堆棧外部指向V8對象,則需要使用包裝器跟蹤。但是,您必須非常小心,不要用它創建參考循環。通常,V8 API很難使用。如果您不確定自己在做什么,請詢問blink-review-bindings @。

 

如果您想了解更多信息:

 

  • 如何使用V8 API和幫助程序類:platform / bindings / HowToUseV8FromBlink.md

V8包裝器

每個C ++ DOM對象(例如Node)都有其對應的V8包裝器。確切地說,每個C ++ DOM對象每個世界都有其對應的V8包裝器。

 

V8包裝器對其相應的C ++ DOM對象有很強的引用。但是,C ++ DOM對象僅對V8包裝程序具有弱引用。因此,如果您想讓V8包裝器存活一段時間,則必須明確地做到這一點。否則,將過早收集V8包裝器,并且V8包裝器上的JS屬性將丟失。

 

div = document.getElementbyId(“ div”);

child = div.firstChild;

child.foo =“酒吧”;

child = null;

GC();?//如果不執行任何操作,則| firstChild |的V8包裝器?由GC收集。

assert(div.firstChild.foo ===“ bar”);?// …這將失敗。

 

如果我們什么都不做,GC會收集child?,因此child.foo?會丟失。為了使div.firstChild?的V8包裝器保持活動狀態,我們必須添加一種機制,“?只要div?所屬的DOM樹可以從V8到達,則使div.firstChild?的V8包裝器保持活動狀態”。

 

有兩種方法可以使V8包裝器保持活動狀態:ActiveScriptWrappable包裝器跟蹤。

 

如果您想了解更多信息:

 

渲染管線

從將HTML文件發送到Blink到在屏幕上顯示像素還有很長的一段路要走。渲染管道的結構如下。

 

閱讀這個出色的資料,以了解渲染管線的每個階段的功能。(我認為我能寫出比甲板更好的解釋🙂

 

如果您想了解更多信息,請聯系:GC收集。

 

assert(div.firstChild.foo ===“ bar”);?// …這將失敗。

 

如果我們什么都不做,GC會收集child,因此child.foo會丟失。為了使div.firstChild的V8包裝器保持活動狀態,我們必須添加一種機制,“只要div所屬的DOM樹可以從V8到達,則使div.firstChild的V8包裝器保持活動狀態”。

 

有兩種方法可以使V8包裝器保持活動狀態:ActiveScriptWrappable和包裝器跟蹤。

 

如果您想了解更多信息:

 

如何管理V8包裝器的生命周期:bindings / core / v8 / V8Wrapper.md

 

如何使用包裝程序跟蹤:platform / bindings / TraceWrapperReference.md

 

渲染管線

從將HTML文件發送到Blink到在屏幕上顯示像素還有很長的一段路要走。渲染管道的結構如下。

Chrome如何顯示網頁

概念應用層

(此插圖的原始Google文檔為http://goo.gl/MsEJX,任何@ chromium.org均可開放對其進行編輯)
每個框代表一個概念性的應用程序層。任何層都不應該了解或依賴任何更高層。
  • WebKit:在Safari,Chromium和所有其他基于WebKit的瀏覽器之間共享的渲染引擎。該港口是WebKit的一部分,與平臺相關的系統集成服務,如資源加載和圖形。
  • 膠水:將WebKit類型轉換為Chromium類型。這是我們的“ WebKit嵌入層”。它是兩個瀏覽器Chromium和test_shell(允許我們測試WebKit)的基礎。
  • 渲染器/渲染主機:這是Chromium的“多進程嵌入層”。它跨流程邊界代理通知和命令。
  • WebContents:一個可重用的組件,它是Content模塊的主要類。它很容易嵌入,以允許將HTML的多進程呈現到視圖中。有關更多信息,請參見內容模塊頁面。
  • 瀏覽器:?代表瀏覽器窗口,其中包含多個WebContent。
  • 選項卡助手:可以附加到WebContents的單個對象(通過WebContentsUserData mixin)。瀏覽器將其中的各種附加到它所擁有的WebContentses(一個用于收藏夾圖標,一個用于信息欄等)。

WebKit

我們使用WebKit開源項目來布局網頁。此代碼從Apple中提取,并存儲在/ third_party / WebKit目錄中。WebKit主要由代表核心布局功能的“ WebCore”和運行JavaScript的“ JavaScriptCore”組成。我們僅出于測試目的運行JavaScriptCore,通常我們將其替換為高性能的V8 JavaScript引擎。我們實際上并沒有使用Apple稱為“ WebKit”的層,它是WebCore和OS X應用程序(例如Safari)之間的嵌入API。為了方便起見,我們通常將Apple的代碼統稱為“ WebKit”。

WebKit端口

在最低級別,我們有WebKit“端口”。這是我們所需的特定于平臺的功能的實現,該功能可與獨立于平臺的WebCore代碼對接。這些文件位于WebKit樹中,通常位于目錄中或作為帶有鉻后綴的文件。我們的許多端口實際上并不是特定于操作系統的:您可以將其視為WebCore的“ Chromium端口”。對于每個平臺,某些部分(如字體渲染)必須以不同的方式處理。

  • 網絡流量是由我們的多進程資源加載系統處理的,而不是直接從渲染過程傳遞給OS的。
  • 圖形使用為Android開發的Skia圖形庫。這是一個跨平臺的圖形庫,可處理除文本以外的所有圖像和圖形基元。Skia位于/?t?hird_party / skia中。圖形操作的主要入口點是/webkit/port/platform/graphics/GraphicsContextSkia.cpp。它使用同一目錄以及/ base / gfx中的許多其他文件。

WebKit膠

與第三方WebKit代碼相比,Chromium應用程序使用不同的類型,編碼樣式和代碼布局。WebKit“膠水”為使用Google編碼約定和類型的WebKit提供了更方便的嵌入API(例如,我們使用std :: string代替WebCore :: StringGURL代替KURL)。粘合代碼位于/ webkit / glue中。膠合對象的名稱通常類似于WebKit對象,但開頭帶有“ Web”。例如,WebCore :: Frame變為WebFrame。

WebKit的“膠水”層將其余的Chromium代碼庫與WebCore數據類型隔離開來,以幫助最小化WebCore更改對Chromium代碼庫的影響。因此,Chromium永遠不會直接使用WebCore數據類型。當Chromium需要戳入某些WebCore對象時,API會被添加到WebKit的“膠水”中。

“測試外殼”應用程序是一個準系統的Web瀏覽器,用于測試我們的WebKit端口和粘合代碼。它使用與Chromium相同的粘合接口與WebKit進行通信。它為開發人員提供了一種更簡單的方法來測試新代碼,而無需具有許多復雜的瀏覽器功能,線程和進程。此應用程序還用于運行自動化的WebKit測試。但是,“測試外殼”的缺點是,它不像Chromium那樣以多進程方式使用WebKit。內容模塊嵌入在稱為“內容外殼”的應用程序中,該應用程序將很快運行測試。

渲染過程

Chromium的渲染過程使用膠水接口嵌入了我們的WebKit端口。它不包含太多代碼:它的工作主要是作為瀏覽器IPC通道的呈現器端。

渲染器中最重要的類是RenderView,它位于/content/renderer/render_view_impl.cc中。該對象表示一個網頁。它處理往返于瀏覽器進程的所有與導航有關的命令。它源自提供繪畫和輸入事件處理的RenderWidget。所述的RenderView經由全球瀏覽器進程通信(每呈現進程)RenderProcess對象。

FAQ:RenderWidget和RenderView有什么區別?RenderWidget通過在稱為WebWidgetDelegate的粘合層中實現抽象接口,映射到一個WebCore :: Widget對象?;旧?,這是屏幕上的一個窗口,用于接收輸入事件并繪制到該窗口中。甲的RenderView從繼承RenderWidget并且是選項卡或彈出窗口的內容。除了小部件的繪制和輸入事件外,它還處理導航命令。僅在一種情況下存在RenderWidget而沒有RenderView?,這是網頁上的選擇框。這些是帶有向下箭頭的框,這些框會彈出一個選項列表。選擇框必須使用本機窗口渲染,以便它們可以顯示在其他所有框的上方,并在必要時彈出框。這些窗口需要繪制并接收輸入,但是沒有單獨的“網頁”(RenderView)。

渲染器中的線程

每個渲染器都有兩個線程(有關圖表,請參見多進程體系結構頁面;有關如何編程,請參見Chromium中的線程)。渲染線程是主要對象(例如RenderView)所在的位置并運行所有WebKit代碼。當它與瀏覽器通信時,消息首先發送到主線程,然后主線程將消息分派給瀏覽器進程。除其他外,這使我們能夠將消息從渲染器同步發送到瀏覽器。發生這種情況的原因是少數操作需要瀏覽器的結果才能繼續。一個示例是在JavaScript請求時獲取頁面的Cookie。渲染器線程將阻塞,并且主線程將對所有收到的消息進行排隊,直到找到正確的響應為止。隨后將任何同時接收到的消息發布到渲染器線程以進行正常處理。

瀏覽器過程

 

低級瀏覽器過程對象

與渲染過程的所有IPC通信都是在瀏覽器的I / O線程上完成的。該線程還處理所有網絡通信,以防止其干擾用戶界面。

在主線程(運行用戶界面的地方)上初始化RenderProcessHost時,它將創建新的渲染器進程和帶有指向渲染器的命名管道的ChannelProxy?IPC對象。該對象在瀏覽器的I / O線程上運行,偵聽到渲染器的命名管道,并自動將所有消息轉發回UI線程上的RenderProcessHost。甲ResourceMessageFilter將被安裝在該通道中,這將篩選出可直接在I / O線程上處理諸如網絡請求的某些消息。此過濾發生在ResourceMessageFilter :: OnMessageReceived中。

所述RenderProcessHost在UI線程上是負責分派所有視圖專用消息到適當的RenderViewHost(它處理非觀看專用消息本身的有限的數目)。這種調度發生在RenderProcessHost :: OnMessageReceived中。

高級瀏覽器過程對象

特定于視圖的消息進入RenderViewHost :: OnMessageReceived。大多數消息都在這里處理,其余消息轉發到RenderWidgetHost基類。這兩個對象映射到渲染器中的RenderViewRenderWidget(有關這些含義,請參見上面的“ Render Process”)。每個平臺都有一個視圖類(RenderWidgetHostView [Aura | Gtk | Mac | Win]),以實現與本機視圖系統的集成。

RenderView?/?Widget上方是WebContents對象,大多數消息實際上最終都作為對該對象的函數調用而結束。一個WebContents代表網頁的內容。它是內容模塊中的頂級對象,并負責以矩形視圖顯示網頁。有關更多信息,請參見內容模塊頁面。

所述WebContents對象被包含在TabContentsWrapper。那是chrome /并負責制表符。

說明性例子

圍繞Chromium源代碼?涵蓋了導航和啟動的其他示例。

“設置游標”消息的壽命

設置光標是從渲染器發送到瀏覽器的典型消息的示例。在渲染器中,將發生以下情況。

  • 設置光標消息是由WebKit在內部生成的,通常是響應輸入事件而生成的。設置光標消息將從content / renderer / render_widget.cc中的RenderWidget :: SetCursor開始。
  • 它將調用RenderWidget :: Send來分派消息。RenderView也使用此方法將消息發送到瀏覽器。它將調用RenderThread :: Send?。
  • 這將調用IPC :: SyncChannel,它將在內部將消息代理到渲染器的主線程,并將其發布到命名管道以發送到瀏覽器。

然后瀏覽器控制:

  • RenderProcessHost中的IPC :: ChannelProxy在瀏覽器的I / O線程上接收所有消息。它首先通過ResourceMessageFilter發送它們,后者直接在I / O線程上調度網絡請求和相關消息。由于我們的消息沒有被過濾掉,因此它將繼續進入瀏覽器的UI線程(IPC :: ChannelProxy在內部執行此操作)。
  • RenderProcessHost :: OnMessageReceived內容/瀏覽器/ renderer_host / render_process_host_impl.cc獲取相應的渲染過程的所有意見的消息。它直接處理幾種類型的消息,其余的則轉發到與發送消息的源RenderView對應的適當的RenderViewHost。
  • 該消息到達RenderViewHost :: OnMessageReceived內容/瀏覽器/ renderer_host / render_view_host_impl.cc。很多消息都在這里處理,但我們是不是因為它是從發送的消息RenderWidget和處理由RenderWidgetHost。
  • RenderViewHost中所有未處理的消息都將自動轉發到RenderWidgetHost,包括我們設置的光標消息。
  • 內容/瀏覽器/renderer_host/render_view_host_impl.cc中的消息映射??最終在RenderWidgetHost :: OnMsgSetCursor中接收到消息,并調用適當的UI函數來設置鼠標光標。

“鼠標單擊”消息的壽命

發送鼠標單擊是從瀏覽器到渲染器的典型消息示例。

  • Windows消息由RenderWidgetHostViewWin :: OnMouseEvent在瀏覽器的UI線程上接收,然后在同一類中調用ForwardMouseEventToRenderer。
  • 轉發器功能將輸入事件打包到一個跨平臺的WebMouseEvent中,并最終將其發送到與之關聯的RenderWidgetHost。
  • RenderWidgetHost :: ForwardInputEvent創建一條IPC消息ViewMsg_HandleInputEvent,將WebInputEvent序列化為其,然后調用RenderWidgetHost :: Send。
  • 這只是轉發給擁有的RenderProcessHost :: Send函數,后者將消息提供給IPC :: ChannelProxy。
  • 在內部,IPC :: ChannelProxy將消息代理到瀏覽器的I / O線程,并將其寫入相應渲染器的命名管道。

請注意,WebContents中還會創建許多其他類型的消息,尤其是導航消息。它們遵循從WebContentsRenderViewHost的相似路徑。

然后渲染器控制:

  • ?渲染器主線程上的IPC :: Channel讀取瀏覽器發送的消息,而IPC :: ChannelProxy代理到渲染器線程。
  • RenderView :: OnMessageReceived獲取消息。許多類型的消息直接在這里處理。由于單擊消息不是,因此它(與所有其他未處理的消息一起)進入RenderWidget :: OnMessageReceived??,后者又將其轉發到??RenderWidget :: OnHandleInputEvent。
  • 將輸入事件提供給??WebWidgetImpl :: HandleInputEvent,在此將其轉換為WebKit?PlatformMouseEvent類,并提供給WebKit?中的WebCore :: Widget類。