NPAPI插件開發學習:Webkit的插件機制

http://blog.csdn.net/zssureqh/article/details/9472677

轉載CSDN博友的一篇關於NPAPI插件機制的博文。

原文地址:http://blog.csdn.net/milado_nju/article/details/7216136

# 插件機制(NPAPI plugin)

## 概述

Chromium中的NPAPI插件(plugin)來源於mozilla的插件機制。因爲它被廣泛的應用,很多插件廠商或者開發者基於它編寫了數以萬計的插件,因而chromium對它也提供了支持,不過chromium有自己獨特的插件架構,後面我們會詳細介紹。

NPAPI提供兩組接口,一類以NPP打頭,由插件來實現,被瀏覽器調用,主要包括一些插件創建,初始化,關閉,銷燬,信息查詢及事件處理,數據流,窗口設置,URL等;另一類以NPN打頭,由瀏覽器來實現,被插件所調用,主要包括圖形繪製,數據流處理,瀏覽器信息查詢,內存分配和釋放,瀏覽器的插件設置,URL等。

原始的NPAPI的接口使用起來不是很方便,因而有貢獻者對其進行了封裝以利於其使用。一個比較著名的開源項目是Firebreath。它將原始的C風格的NPAPI進行封裝成C++風格的接口,非常方便用戶使用,而且有針對Windows和X window的移植,用戶無需對底層特別瞭解。特別的是,Firebreath也有對ActiveX的封裝,因而對於現在主流的兩種插件接口,你都可以基於Firebreath的接口進行編程,極大地方便了開發者。詳情請參考Firebreath主頁http://www.firebreath.org/display/documentation/FireBreath+Home

 ## 架構

因爲chromium的安全模型,renderer進程沒有訪問除I/O讀寫等之外的權限,因而插件需要有自己的進程,這就是插件的out-of-process模型。下圖給出chromium中插件的架構和進程模型。


每一種類型的plugin只有一個進程,這就是說,如果有兩個或者多個renderer進程同時使用同一個插件,那麼該插件會共享同一個進程。因爲多個renderer進程共享同一種的plugin進程,那麼plugin進程如何爲它們服務呢?答案是爲每個插件使用點在plugin進程中創建一個插件實例(PluginInstance).

值得注意的是,plugin進程是由browser進程來負責創建和銷燬, 而不是renderer進程。 原因在於renderer進程沒有創建的權限,而且plugin進程由browser進程來統一管理也更方便。 當plugin進程創建成功時,browser進程會返回IPCchannel handle用於創建和plugin進程通訊的PluginChannelHost. 那它什麼時候被銷燬呢?當沒有任何插件實例並且空閒一段事件後,它纔會被銷燬,這樣做的好處是避免頻繁的創建和銷燬plugin進程。

下圖描述了browser和plugin進程間的通訊機制及其所涉及的相關的模塊(類)。Browser進程通過PluginProcessHost發送消息調用Plugin進程的函數,響應動作由PluginThread完成。而Plugin進程則是通過WebPluginProxy發送消息調用browser的函數,響應動作有PluginProcessHost完成。


Browser和Plugin僅有較少的消息傳遞,用於創建等管理工作。主要的部分在renderer進程和plugin進程之間,機制也相對更復雜一些。HTMLPluginElement會包含一個WebPluginContainerImpl,而它包含一個WebPluginImpl,對plugin的調用有WebPluginDelegateProxy負責中轉。在Plugin進程中,由WebPluginDelegateStub處理所有renderer過來的請求,並由WebPluginDelegateImpl調用創建好的PluginInstance對象。PluginInstance最終調用PluginLib讀取的插件的函數入口地址,最終完成對插件實現的調用。而對插件實現中對NPN開頭函數的調用,則是通過PluginHost來完成。

PluginHost主要負責實現NPN開頭的函數,如前面所描述,這些函數被plugin進程所調用。可以在plugin和renderer進程被調用。當在plugin進程調用這些函數時,chromium會覆蓋PluginHost的部分函數,而這些新的callback函數會調用NPObjectProxy來通過IPC發送請求到renderer進程。

PluginInstance實現了NPP開頭的函數,被render所調用(webpluginImpl通過webplugindelegateImpl來調用),PluginInstance通過PluginLib獲得了插件庫的函數地址,從而把實際的調用橋接到具體的插件中。

具體的見下圖所示,主要結構來源於chromium的官網,略有修改。


對於NPObject相關的函數調用, 有專門的類來處理。NPObject的調用或者訪問是雙向的(renderer進程<->plugin進程),他們的具體實現是通過NPObjectProxy和NPObjectStub來完成。 NPObjectProxy接受來自對方的訪問請求,轉發給NPObjectStub,最後NPObjectStub調用真正的NPObject並返回結果。見下圖所示。


下面示例給出一個插件如何被renderer進程觸發創建的過程。


當頁面中包含一個“embed”或者“object”元素,renderer進程會創建一個HTMLEmbedElement元素,當該元素被訪問是,會觸發創建相應的插件。HTMLEmbedElement會請求創建自己對應的RenderWidget(WebPluginContainerImpl),進而創建WebPluginImpl和WebPluginDelegateProxy。WebPluginDelegateProxy會發送消息來創建Plugin進程。Plugin進程被browser進程創建後,會響應renderer的請求來創建PluginInstance並初始化它。這樣它們之間的聯繫就建立好了。

##  Window和windowless插件

可以通過設置”embed”或者”object”元素的屬性。兩者的區別主要在於繪圖的方式不同。Window插件由renderer進程提供一個窗口(window), 插件直接在該窗口上進行繪製;而windowless插件則不同,插件將繪製的結構(Pixmap),通過共享內存方式(Transport DIB)傳遞給renderer進程,renderer繪製該內容到自己內部的存儲結構(backing store)上。好處是,可以把插件繪製的結構和網頁上的其他內容做各種形式的合成。但是,從上面不能看出,一般來講,window模式的性能是要高於windowless的。

## 相關目錄和文件

third_party/WebKit/Source/WebKit/chromium/public/webplugin.h:

         定義Webkit::WebPlugin接口,用於創建和銷燬插件,及傳送事件給插件

content/common/plugin_messages.h:

         定義plugin進程與renderer進程和browser進程間的消息結構體,包括五類:

PluginProcessMsg開頭的消息- browser進程發給plugin進程

PluginProcessHostMsg開頭的消息- plugin進程發給browser進程

PluginMsg開頭的消息- renderer進程發給plugin進程

PluginHostMsg開頭的消息- plugin進程發給renderer進程

NPObjectMsg開頭的消息- plugin進程與render進程相互編碼和解碼NPObject

content/common/npobject_stub.(h&cc):

                   類NPObjectStub的定義和實現

content/common/npobject_Proxy.(h&cc):

                   類NPObjectProxy的定義和實現

content/plugin:

         該目錄用於存放Plugin進程使用的IPC和WebPlugin相關的函數和類

content/plugin/webplugin_proxy.(h&cc):

                   實現WebKit::npapi::WebPlugin接口,把插件的調用通過IPC發送給renderer進程

content/plugin/webplugin_delegate_stub.(h&cc):

                   把WebPluginDelegateProxy的消息轉換爲對WebPluginDelegateImpl的調用

content/plugin/plugin_channel.(h&cc):

                   定義Plugin進程與renderer進程通信的通道

webkit/plugins/npapi/:

         該目錄用於存放Plugin進程使用的對WebKit接口實現和插件庫處理的相關的函數和類

webkit/plugins/npapi/webplugin.h:

                   定義WebPlugin接口,用於plugin端同web frame和webcore對象的交互

webkit/plugins/npapi/webplugin_impl.(h&cc):

                   實現WebKit::WebPlugin和WebPlugin接口,通過WebPluginPageDelegate來創建WebPluginDelegate

webkit/plugins/npapi/plugin_instance.(h&cc)

                   PluginInstance類的接口和實現,代表一個Plugin實例,對應於renderer進程的一個請求創建的plugin實例

webkit/plugins/npapi/webplugin_delegate.h:

                   定義WebPlugin代理,用來分離具體的插件的實現方式,例如可以使來實現進程內或者跨進程的插件架構,對WebPlugin來說,具體架構是透明的

webkit/plugins/npapi/webplugin_delegate_impl.(h&cc):

                   響應renderer進程實現對PluginInstance的調用請求,有gtk,win和aura三種不同的實現

webkit/plugins/npapi/plugin_host.(h&cc):

                   實現NPN開頭的函數,在plugin進程和renderer進程有不同的實現

webkit/plugins/npapi/plugin_lib.(h&cc):

                   PluginLib用來管理實際插件庫的生命週期

content/renderer/webplugin_delegate_proxy.(h&cc)

                   定義和實現WebPluginDelegateProxy類,橋接所有來自於WebPlugin的請求到WebPluginDelegateImpl

content/browser/plugin_process_host.(h&cc):

                   類PluginProcessHost的定義和實現,用於Browser進程與plugin進程的交互

content/browser/plugin_service_impl.(h&cc):

                   類PluginServiceImpl的定義和實現,用於響應Renderer進程創建插件請求及其他一些插件管理工作

## 參考文檔:

http://www.chromium.org/developers/design-documents/plugin-architecture

 By [email protected]


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章