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

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

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

# 插件機制(NPAPI plugin)api

## 概述瀏覽器

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+Homeless

 ## 架構函數

由於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 yongsheng@chromium.org

相關文章
相關標籤/搜索