理解PPAPI的设计

最后更新于:2022-04-01 16:02:47

要理解PPAPI插件的设计,先仔细阅读下面这些文章: - [**Chromium的Process Models**](http://www.chromium.org/developers/design-documents/process-models) - [**Chromium的Multi-process Architecture**](http://www.chromium.org/developers/design-documents/multi-process-architecture) - [**Chromium的Plugin Architecture**](http://www.chromium.org/developers/design-documents/plugin-architecture) - [**Pepper plugin implementation**](http://www.chromium.org/developers/design-documents/pepper-plugin-implementation) 理解了架构设计,再看代码层面的文档: - [**Important concepts for working with PPAPI**](https://code.google.com/p/ppapi/wiki/Concepts) - [**GettingStarted, Writing a simple plugin**](https://code.google.com/p/ppapi/wiki/GettingStarted) 有的链接需要翻墙,天朝的局域网,我爱死你了。 好啦,现在对PPAPI应该有基本的理解了。接下来我从代码角度来理解一下。 # Module、Instance、Interface HTML页面可以通过embed标签来嵌入一个插件,HTML页面被加载时,解析到embed标签,就会根据type属性定位我们注册的PPAPI插件,加载对应的插件库(DLL)。 当PPAPI的库文件(DLL)被加载到浏览器进程中时,一个Module就产生了。在代码中,通过PP_Module(定义在pp_module.h中)来表示,用于标识一个Module的PP_Module类型实际上是一个int32。Module的标识符通过PPP_InitializeModule函数传入。PPP_InitializeModule函数的原型如下: ~~~ int32_t PPP_InitializeModule(PP_Module module, PPB_GetInterface get_browser_interface) ; ~~~ 第一个参数就是浏览器分配给你的插件库文件的标识符(handle),一般你需要保存它,后续的有些API会用到。 所以,一个Module,仅仅标识了library。要想在浏览器上显示点什么,还需要从这个Module里创建一个实例,这个实例代表了我们可以看见并与之交互的网页对象。一个插件实例对象又有两个层面的属性,一个就是标示符,通过PP_Instance(32位整型)来表示;另外一个是用来操作实例对象的接口,用PPP_Instance表示(聚合了各种函数指针的结构体)。 PPAPI的plugin会导出PPP_GetInterface函数,其原型如下: ~~~ const void* PPP_GetInterface(const char* interface_name); ~~~ 当浏览器要为HTML页面创建插件实例时,会先调用PPP_GetInterface函数获取一个实例模板指针(可以理解为PPP_Instance的实例,类似一个类,实际上是一个定义了函数指针成员的结构体)。 PPP_GetInterface函数接受一个字符串名字,返回void*,浏览器拿到万能的void*后会根据名字转换为具体的PPP_instance接口,在后续使用中就通过PPP_instance接口来与插件实例交互(比如具体的创建、销毁等动作)。 简单的理解,就是PPP_GetInterface会返回能创建Instance的接口PPP_Instance,浏览器调用PPP_Instance来创建实例并与实例交互。 PPP_instance接口声明如下: ~~~ struct PPP_Instance_1_1 { PP_Bool (*DidCreate)(PP_Instance instance, uint32_t argc, const char* argn[], const char* argv[]); void (*DidDestroy)(PP_Instance instance); void (*DidChangeView)(PP_Instance instance, PP_Resource view); void (*DidChangeFocus)(PP_Instance instance, PP_Bool has_focus); PP_Bool (*HandleDocumentLoad)(PP_Instance instance, PP_Resource url_loader); }; ~~~ 如你所见,它就像一个类,DidCreate是构造函数指针,DidDestroy是析构函数指针。创建插件实例对象时,DidCreate会被调用,其第一个参数instance,就是浏览器分配给这个插件实例对象的句柄(32位整数),通常我们可以保存起来供后续的调用使用。具体的说明,可以看ppp_instance.h。 之前在[**VS2013编译最简单的PPAPI插件**](http://blog.csdn.net/foruok/article/details/50485461)中我们编译了stub插件,它的PPP_GetInterface函数返回NULL,所以,其实浏览器可以加载stub库文件,生成Module,但无法创建Instance。 要想实作一个有用的PPAPI plugin,必须在PPP_GetInterface中返回真实的PPP_instance接口。下面是graphics_2d_example.c里的PPP_GetInterface函数实现: ~~~ PP_EXPORT const void* PPP_GetInterface(const char* interface_name) { if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) return &instance_interface; return NULL; } ~~~ 它返回的instance_interface,是这么定义的: ~~~ static PPP_Instance instance_interface = { &Instance_DidCreate, &Instance_DidDestroy, &Instance_DidChangeView, &Instance_DidChangeFocus, &Instance_HandleDocumentLoad }; ~~~ 如你所见,它是一个PPP_Instance,在定义时进行了初始化,把文件内实现的几个函数,赋值给了结构体的5个函数指针。 # 浏览器接口PPB_GetInterface PPAPI插件要与浏览器交互,也得先有渠道来获取浏览器的功能接口。浏览器提供了很多功能接口,比如PPB_INSTANCE_INTERFACE,PPB_IMAGEDATA_INTERFACE,PPB_GRAPHICS_2D_INTERFACE等。 这些宏都是字符串,浏览器提供的接口名字宏,以PPB_开头,插件提供的,以PPP_开头。 插件被加载时,模块初始化函数PPP_InitializeModule会被调用,其原型如下: ~~~ int32_t PPP_InitializeModule(PP_Module module_id, PPB_GetInterface get_browser_interface); ~~~ 注意第二个参数,get_browser_interface,它的类型是PPB_GetInterface,是一个函数指针,定义如下: ~~~ typedef const void* (*PPB_GetInterface)(const char* interface_name); ~~~ 如你所见,这是一个接受一个字符串参数返回void*的函数指针。插件可以在PPP_InitializeModule被调用时保存第一个参数,用它来获取浏览器提供的各种接口。根据接口名字,把返回的void*强制转换为对应的接口来使用。参看graphics_2d_example.c里的实现: ~~~ PP_EXPORT int32_t PPP_InitializeModule(PP_Module module, PPB_GetInterface get_browser_interface) { g_get_browser_interface = get_browser_interface; g_core_interface = (const PPB_Core*) get_browser_interface(PPB_CORE_INTERFACE); g_instance_interface = (const PPB_Instance*) get_browser_interface(PPB_INSTANCE_INTERFACE); g_image_data_interface = (const PPB_ImageData*) get_browser_interface(PPB_IMAGEDATA_INTERFACE); g_graphics_2d_interface = (const PPB_Graphics2D*) get_browser_interface(PPB_GRAPHICS_2D_INTERFACE); g_view_interface = (const PPB_View*) get_browser_interface(PPB_VIEW_INTERFACE); if (!g_core_interface || !g_instance_interface || g_image_data_interface || !g_graphics_2d_interface || !g_view_interface) return -1; return PP_OK; } ~~~ 当我们拿到了浏览器暴露的各种接口,就可以做想干的事情了。 对于PPAPI插件的设计,先理解到这里,下次我们看插件的加载与使用流程、如何绘图、如何处理交互。 相关文章参考: - [**CEF Windows开发环境搭建**](http://blog.csdn.net/foruok/article/details/50468642) - [**CEF加载PPAPI插件**](http://blog.csdn.net/foruok/article/details/50485448) - [**VS2013编译最简单的PPAPI插件**](http://blog.csdn.net/foruok/article/details/50485461)
';