接上一节的内容,但初始化插件以后(NP_Initialize),WebKit将实例化插件,调用
NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
char* argn[], char* argv[], NPSavedData* saved);
所以上一节提到的NP前缀,我认为可以理解成类函数,而NPP前缀相当于实例函数了,这里的NPP,就可以看成是this指针,或者C里面的叫法Handle了,看NPP的定义
typedef struct _NPP
{
void* pdata; /* plug-in private data */
void* ndata; /* netscape private data */
} NPP_t;
typedef NPP_t* NPP;
也证实了这一点,对于它就是简单的使用它的pdata来传递参数吧
先来看看NPP_New里面的实现
instance->pdata = browser->createobject (instance, getPluginClass());
其中browser是前面初始化的时候传过来的浏览器接口,利用它来实例化插件,getPluginClass是干嘛的?
这里的getpluginClass返回NPClass指针,其中定义了插件的一系列函数指针(又是函数指针。。。。),下面是一个实现的例子:
static NPClass pluginClass = {
NP_CLASS_STRUCT_VERSION,
pluginAllocate,
pluginDeallocate,
pluginInvalidate,
pluginHasMethod,
pluginInvoke,
pluginInvokeDefault,
pluginHasProperty,
pluginGetProperty,
pluginSetProperty,
pluginRemoveProperty,
pluginEnumerate
};
这些函数指针应该是预先定义好的,顺序,个数都要保证,由于以前开发过相关的这部分内容,我知道这些是浏览器用来操作NPObject的接口,任意一个js对象,调用它的属性或者方法,最终都是调用到对应的NPObject中来的,这样来看,这个NPObject代表了对应的一个js的对象,我们这里就是对应了MIME那个节点
浏览器知道了这个NPObject的接口,就直接利用pluginAllocate建立这个NPObject,回调。。。又是回调,插件的地位很低的,告诉别人怎么样调用自己,但啥时候被调不是自己说了算的
实际中,我们创建了一个PluginObject,这个相当于是插件这边的Handle,里面保存了很多东东:
typedef struct PluginObject {
NPObject header;
NPP npp;
NPWindow* window;
PluginType pluginType;
SubPlugin* activePlugin;
} PluginObject;
其中,NPObject,NPP在pluginAllocate中赋值:
static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
{
PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
newInstance->header._class = theClass;
newInstance->header.referenceCount = 1;
if (!identifiersInitialized) {
identifiersInitialized = true;
initializeIdentifiers();
}
newInstance->npp = npp;
return &newInstance->header;
}
这里可以看到,NPObject就是NPClass的封装,代表了js的对象本质就是一组函数
NPWindow在哪里赋值的?还记得NPP_SetWindow吧,这个应该是在插件视图创建好以后调用的
嗯,还没有完,继续NPP_New,看看这3个参数
int16 argc,char* argn[], char* argv[]
类似main函数的参数,argn和argv分别代表节点的属性名和属性值,我对HTML不是很熟,不知道是不是叫这个,还是对照测试用的HTML文件来解释好一些:
<object type="application/x-testbrowserplugin" height=200 width=400 id="sample">
<param name="DrawingModel" value="Surface" />
<param name="PluginType" value="Paint" />
</object>
OK,这里的argc == 2,argn[0] == "DrawingModel",argn[1] == "Surface",这样大家应该就明白了
浏览器通过HTML中插件的参数,调用NPP_New初始化插件,得到SubPlugin和PluginType
这里的SubPlugin代表真正的插件实例的接口,定义如下:
class SubPlugin {
public:
SubPlugin(NPP inst) : m_inst(inst) {}
virtual ~SubPlugin() {}
virtual int16 handleEvent(const ANPEvent* evt) = 0;
virtual bool supportsDrawingModel(ANPDrawingModel) = 0;
int getPluginWidth();
int getPluginHeight();
NPP inst() const { return m_inst; }
private:
NPP m_inst;
};
class SurfaceSubPlugin : public SubPlugin {
public:
SurfaceSubPlugin(NPP inst) : SubPlugin(inst) { m_context = NULL; }
virtual ~SurfaceSubPlugin() {}
virtual jobject getSurface() = 0;
virtual bool supportsDrawingModel(ANPDrawingModel);
void setContext(jobject context);
jobject m_context;
};
SubPlugin,提供处理事件接口,SurfaceSubPlugin继承SubPlugin,增加了Context,用作jni相关的调用
实际中,我们继承SurfaceSubPlugin,实现这些纯虚函数,创建我们自己的插件,而HandleEvent的事件就是前面讲到了的在NPP_HandleEvent中传入的
OK,到现在为止,初始化的工作做完了,基本的调用逻辑,框架也搭好了,可以开始具体的插件实现了