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

相关文章
相关标签/搜索