1. 程式人生 > >Chrome原始碼剖析-- Chrome的外掛模型

Chrome原始碼剖析-- Chrome的外掛模型

1. NPAPI

為了緊密的與各個開源瀏覽器團結起來,共同抗擊IE的壟斷,Chrome的外掛,也遵循了NPAPI(Netscape Plugin Application Programming Interface)標準,支援這個標準的瀏覽器需要實現一組規定的API供外掛呼叫,這組API形如NPN_XXX,比如NPN_GetURL,外掛可以利用這些API進行二次開發。而NPAPI外掛以一個Dll之類的作為物理載體(windows下dll,linux下是so...)進行提供,裡面同樣也實現了一組規定的API。形式包括NP_XXXNPP_XXX,NP_XXX是系統需要預設呼叫的方法,用於認知這個外掛,比如NP_Initialize, 而NPP_XXX是用於外掛完成一些實際功能,比如NPP_New。。。

所有的外掛dll都需要放置在指定目錄下(根據作業系統的不同而不同...),每個外掛可以處理一種或多種MIME格式的資料,比如application/pdf,說明該外掛可以處理pdf相關的文件。在Chrome中鍵入about:plugins,可以檢視當前Chrome中具有的外掛資訊。。。

NPAPI是一個很經典的外掛方案,用dll進行注入,用協定的API進行通訊,用字串描述外掛能力。外掛宿主(在這裡就是瀏覽器...),會根據能力描述,動態載入外掛,並負責外掛呼叫的流程和生命週期管理。而外掛中,負責真實邏輯的處理,並可以構造UI與使用者交流。以此類方式實現的外掛系統,往往是處理的邏輯比較固定適用範圍一般(用API寫死了邏輯...),但可擴充套件性不錯(用字串描述能力,可無限擴充套件...)。。。

在Chrome中nphostapi.h中,定義了所有NPAPI相關的函式指標和結構,這個檔案放置在glue目錄下,如果看過前面碰過的文章就知道,在WebKit內肯定也有一套相同的東西;在npapi.h/.cc中,提供了Chrome瀏覽器端的NPN_XXX系列函式的實現;每一個外掛物理例項,用PluginLib類來表示,而每一個外掛的邏輯例項,用PluginInstance類來表示。這個概念牽強附會的可以用windows中的控制代碼來類比,當你想操作一個核心物件,你需要獲得一個核心物件的控制代碼,每個程序中的控制代碼肯定不相同,但後面的核心物件卻是同一個,核心物件的生命週期通過控制代碼的計數來控制,有人用則或,無人用則死(當然這個類比相當的牽強,主要是想說明引用計數和邏輯與物理的關係,但一個關鍵性的區別在於,PluginLib與PluginInstance都是在一個程序內的

,不能跨越程序邊界...)。在Chrome中,PluginLib負責載入和銷燬一個dll,拿到所有匯出函式的函式指標,PluginInstance對這些東西進行了封裝,可以更好的來呼叫。。。

關於NPAPI的更多細節,Chrome並沒有提供任何文件,但是,各個先驅的瀏覽器們都提供了大量豐富的文件。比如,你可以到 這裡,檢視firefox中的NPAPI文件,基本通用。。。

2. Chrome的多程序外掛模型

Chrome的外掛模型,與早先的瀏覽器的最大不同,是它採用了多程序的方式,每一個外掛,都有一個單獨的程序來承載(Shift + Esc開啟Chrome程序管理器,可以看到現在已經載入的外掛程序...)。當WebKit進行頁面渲染的時候,發現了未知的MIME型別資料,它會告知給Browser程序,召喚它提供一個外掛來解析。如果該外掛還未載入,Browser會在指定目錄中搜尋出具有此實力的外掛(如果沒有此類人才只能作罷...),併為它建立一個程序,讓它負責所有的該外掛相關的任務,然後建立起一個IPC通路,與它“保持通話”。這套流程一定不會太陌生,因為它與Render程序的建立大同小異換湯不換藥。。。

Plugin程序與Render程序最大的區別在於,Render需要與Browser程序大量通訊,因為它的HWND歸Browser老大掌管著,相關所有內容都需要通訊完成。但Plugin不需要與Browser頻繁聯絡,它大部分的通訊都是與Render程序發生的。如果Plugin與Render之間的通訊,還需要走Browser中轉一下,這就顯得有些脫褲子放屁了,雖然Browser是大頭,但不是冤大頭,它不會幹這種吃力不討好的事情。他只是做了一回Render與Plugin間的媒婆而已。當Plugin與Browser建立好了IPC通路後,它會讓Render建立一個新IPC通路,用以與Plugin通訊,IPC的有名管道名,經由Browser通知給Plugin。完成名字協商後,Render與Plugin的通訊關係就建立好了,它們之間就可以直接進行通訊了。。。

整個通訊模式,可以看 這裡 。這是一個很標準的代理模式的應用,稍有了解的都可以跳過我後面會做的一段羅嗦的描述,一看官方文件中的圖便能知曉。在Render程序端,WebPluginImpl是WebPlugin的一個子類,WebPlugin是供Webkit進行呼叫的一個介面,利用依賴倒置,實現了擴充套件。在Plugin程序端,實現了一個WebPluginDelegateImpl類,該類會呼叫PluginInstance的相關介面實現真實的外掛功能。這樣的話,只需要WebPluginImpl呼叫WebPluginDelegateImpl中的相應方法,就可以實現功能。但問題是WebPluginImpl與WebPluginDelegateImpl天各一方各處於一個程序,很顯然,這裡需要一個代理模式。這裡沿用了COM的架構,Delegate + Stub + Proxy。WebPluginImpl呼叫代理WebPluginDelegateProxy,該代理會將呼叫轉換成訊息,通過IPC傳送給Plugin程序,在Plugin端,通過WebPluginDelegateStub監聽訊息,並轉換成對真實WebPluginDelegateImpl的呼叫,從而完成了跨程序的一個呼叫,反之亦然。。。

3. Chrome的可擴充套件性

總所周知,firefox通過三種方式進行自定義,外掛、擴充套件和面板。其中,外掛是使得瀏覽器能用,不會出現一大塊一大塊的無法顯示的區域;擴充套件是使得瀏覽器好用,可以簡單方便的進行功能的定製和個性化配置;面板是幫助瀏覽器變得好看,畢竟羅卜白菜,給有所愛。。。

與之對比,來看Chrome。Chrome有了外掛,有了面板,但是沒有擴充套件。這就意味著,你很難為Chrome定製一些特色的功能。目前,所有對Chrome的功能擴充套件,都是通過書籤抑或是修改核心來實現的。前者能力太弱,後者開發起來太麻煩,容易出錯不提,還必須要與時俱進,跟上版本的變化,並且還不能自由的選擇或關閉。因此,這都不是長遠之計,Chrome提供一套類似於firefox的擴充套件機制,也許才是正道。據傳說,Chrome團隊正在琢磨這件事,不知道最終會出來個怎麼樣的結果,是盡力接近firefox降低移植成本,還是另立門戶特立獨行,我想可以拭目以待一把。。。

在多程序模式下,Chrome的外掛還有一個問題,前面提到過,就是關於UI控制元件的。由於NPAPI的標準,是允許外掛建立HWND視窗的,這就使得當Plugin繁忙,且Browser程序發起HWND的同步的時候,主程序被掛起,這個瀏覽器停滯。在Render程序中,解決這個問題的思路是控制權限,不然Render建立HWND,到了Plugin中,這招不能使用,只能夠使用另一招,就是監管。不停的檢查Plugin是否太繁忙,無法響應,一旦發現,立即殺死該Plugin及其所處的頁面。這就好比你想解決奶中有三氯氰胺的問題,要麼控制奶源,不從奶站購買全部用自家的,要麼加強監管,提高檢查力度防止隱患。兩種策略的優缺點一眼便知,依照不同環境採取不同策略即可。。。

總體說來,Chrome的可擴充套件性著實一般,不過Chrome還處於Beta中,我們可以繼續期待。。。