Posted on 2009-05-18 15:59
eyesmart 阅读(5746)
评论(2) 编辑 收藏 引用 所属分类:
Basic Knowledge
最近开发了一个firefox plugin,有点心得,决定将其写下来。因为plugin的一些规则比较死板,所以了解其中函数的关系和调用的先后顺序还要参考https://developer.mozilla.org/en/Plugins,本文主要讨论Windows,Linux,MAC OS X三个平台间的差异以及安装更新的过程。
plugin其实是一个可执行的文件(动态库),主要用于扩充浏览器对数据的解释能力,如flash,pdf格式,也可以用于与本机其他模块或对象进行通信,如具有脚本访问功能的plugin。因为我想开发一个具有脚本访问功能的plugin,所以从firefox源代码包中我选择了npruntime,因为这些结构都是比较固定的。
构建npruntime之前要安装SDK,新建工程或makefile,设置一些宏定义,如XP_WIN=1,这个是很容易成功的。要是npruntime跨平台还需要修改其代码,经多次摸索以及google,我提炼出完整的接口如下:
#include "npapi.h"
#include "npupp.h"
char* NPP_GetMIMEDescription();
#ifdef XP_MACOSX
extern "C" {
NP_EXPORT(NPError) OSCALL NP_Initialize(NPNetscapeFuncs* pFuncs
#ifdef XP_UNIX
, NPPluginFuncs* pluginFuncs
#endif // XP_UNIX
);
NP_EXPORT(NPError) OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs);
NP_EXPORT(NPError) OSCALL NP_Shutdown(void);
NP_EXPORT(int) main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs,
NPP_ShutdownUPP* unloadUpp);
}
#endif // XP_MACOSX
#ifndef HIBYTE
#define HIBYTE(x) ((((uint32_t)(x)) & 0xff00) >> 8)
#endif
NPNetscapeFuncs NPNFuncs;
#if defined(XP_WIN) || defined(XP_MACOSX)
NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
printf("GetEntryPoints\n");
if (pFuncs == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
#ifdef XP_MACOSX
// Webkit under OS X passes 0 in pFuncs->size, and Apple's sample code
// (NetscapeMoviePlugIn) treats this as an output parameter.
if (pFuncs->size == 0)
pFuncs->size = sizeof(NPPluginFuncs);
#endif // XP_MACOSX
if (pFuncs->size < sizeof(NPPluginFuncs))
return NPERR_INVALID_FUNCTABLE_ERROR;
pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
pFuncs->newp = NPP_New;
pFuncs->destroy = NPP_Destroy;
pFuncs->setwindow = NPP_SetWindow;
pFuncs->newstream = NPP_NewStream;
pFuncs->destroystream = NPP_DestroyStream;
pFuncs->asfile = NPP_StreamAsFile;
pFuncs->writeready = NPP_WriteReady;
pFuncs->write = NPP_Write;
pFuncs->print = NPP_Print;
pFuncs->event = NPP_HandleEvent;
pFuncs->urlnotify = NPP_URLNotify;
pFuncs->getvalue = NPP_GetValue;
pFuncs->setvalue = NPP_SetValue;
pFuncs->javaClass = NULL;
return NPERR_NO_ERROR;
}
#endif // defined(XP_WIN) || defined(XP_MACOSX)
char*
NP_GetMIMEDescription()
{
printf("GetMIMEDescription\n");
return NPP_GetMIMEDescription();
}
NPError
NP_GetValue(void* future, NPPVariable variable, void *value)
{
return NPP_GetValue((NPP_t *)future, variable, value);
}
// Under OS X, we rely on NP_GetEntryPoints() to be called to set up
// the NPPluginFuncs structure, and we do not use the second parameter
// `pluginFuncs` here for the compatibility with Safari under OS X.
NPError OSCALL
NP_Initialize(NPNetscapeFuncs* pFuncs
#ifdef XP_UNIX
, NPPluginFuncs* pluginFuncs
#endif // XP_UNIX
)
{
printf("Initialize\n");
if(pFuncs == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)
return NPERR_INCOMPATIBLE_VERSION_ERROR;
NPNFuncs.size = pFuncs->size;
NPNFuncs.version = pFuncs->version;
NPNFuncs.geturlnotify = pFuncs->geturlnotify;
NPNFuncs.geturl = pFuncs->geturl;
NPNFuncs.posturlnotify = pFuncs->posturlnotify;
NPNFuncs.posturl = pFuncs->posturl;
NPNFuncs.requestread = pFuncs->requestread;
NPNFuncs.newstream = pFuncs->newstream;
NPNFuncs.write = pFuncs->write;
NPNFuncs.destroystream = pFuncs->destroystream;
NPNFuncs.status = pFuncs->status;
NPNFuncs.uagent = pFuncs->uagent;
NPNFuncs.memalloc = pFuncs->memalloc;
NPNFuncs.memfree = pFuncs->memfree;
NPNFuncs.memflush = pFuncs->memflush;
NPNFuncs.reloadplugins = pFuncs->reloadplugins;
NPNFuncs.getJavaEnv = pFuncs->getJavaEnv;
NPNFuncs.getJavaPeer = pFuncs->getJavaPeer;
NPNFuncs.getvalue = pFuncs->getvalue;
NPNFuncs.setvalue = pFuncs->setvalue;
NPNFuncs.invalidaterect = pFuncs->invalidaterect;
NPNFuncs.invalidateregion = pFuncs->invalidateregion;
NPNFuncs.forceredraw = pFuncs->forceredraw;
NPNFuncs.getstringidentifier = pFuncs->getstringidentifier;
NPNFuncs.getstringidentifiers = pFuncs->getstringidentifiers;
NPNFuncs.getintidentifier = pFuncs->getintidentifier;
NPNFuncs.identifierisstring = pFuncs->identifierisstring;
NPNFuncs.utf8fromidentifier = pFuncs->utf8fromidentifier;
NPNFuncs.intfromidentifier = pFuncs->intfromidentifier;
NPNFuncs.createobject = pFuncs->createobject;
NPNFuncs.retainobject = pFuncs->retainobject;
NPNFuncs.releaseobject = pFuncs->releaseobject;
NPNFuncs.invoke = pFuncs->invoke;
NPNFuncs.invokeDefault = pFuncs->invokeDefault;
NPNFuncs.evaluate = pFuncs->evaluate;
NPNFuncs.getproperty = pFuncs->getproperty;
NPNFuncs.setproperty = pFuncs->setproperty;
NPNFuncs.removeproperty = pFuncs->removeproperty;
NPNFuncs.hasproperty = pFuncs->hasproperty;
NPNFuncs.hasmethod = pFuncs->hasmethod;
NPNFuncs.releasevariantvalue = pFuncs->releasevariantvalue;
NPNFuncs.setexception = pFuncs->setexception;
// NPNFuncs.pluginthreadasynccall = pFuncs->pluginthreadasynccall;
#if defined(XP_UNIX) && !defined(XP_MACOSX)
/**//*
* Set up the plugin function table that Netscape will use to
* call us. Netscape needs to know about our version and size
* and have a UniversalProcPointer for every function we
* implement.
*/
pluginFuncs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
pluginFuncs->size = sizeof(NPPluginFuncs);
pluginFuncs->newp = NewNPP_NewProc(NPP_New);
pluginFuncs->destroy = NewNPP_DestroyProc(NPP_Destroy);
pluginFuncs->setwindow = NewNPP_SetWindowProc(NPP_SetWindow);
pluginFuncs->newstream = NewNPP_NewStreamProc(NPP_NewStream);
pluginFuncs->destroystream = NewNPP_DestroyStreamProc(NPP_DestroyStream);
pluginFuncs->asfile = NewNPP_StreamAsFileProc(NPP_StreamAsFile);
pluginFuncs->writeready = NewNPP_WriteReadyProc(NPP_WriteReady);
pluginFuncs->write = NewNPP_WriteProc(NPP_Write);
pluginFuncs->print = NewNPP_PrintProc(NPP_Print);
pluginFuncs->urlnotify = NewNPP_URLNotifyProc(NPP_URLNotify);
pluginFuncs->event = NewNPP_HandleEventProc(NPP_HandleEvent);
pluginFuncs->getvalue = NewNPP_GetValueProc(NPP_GetValue);
#ifdef OJI
pluginFuncs->javaClass = NPP_GetJavaClass();
#endif // OJI
#endif // defined(XP_UNIX) && !defined(XP_MACOSX)
return NPP_Initialize();
}
NPError OSCALL NP_Shutdown()
{
NPP_Shutdown();
return NPERR_NO_ERROR;
}
#ifdef XP_MACOSX
int main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp)
{
NPError err = NPERR_NO_ERROR;
printf("main\n");
//
// Ensure that everything Netscape passed us is valid!
//
if ((nsTable == NULL) || (pluginFuncs == NULL) || (unloadUpp == NULL))
err = NPERR_INVALID_FUNCTABLE_ERROR;
//
// Check the "major" version passed in Netscape's function table.
// We won't load if the major version is newer than what we expect.
// Also check that the function tables passed in are big enough for
// all the functions we need (they could be bigger, if Netscape added
// new APIs, but that's OK with us -- we'll just ignore them).
//
if (err == NPERR_NO_ERROR)
{
if (HIBYTE(nsTable->version) > NP_VERSION_MAJOR)
err = NPERR_INCOMPATIBLE_VERSION_ERROR;
}
if (err == NPERR_NO_ERROR)
{
err = NP_Initialize(nsTable, NULL);
}
if (err == NPERR_NO_ERROR)
{
err = NP_GetEntryPoints(pluginFuncs);
*unloadUpp = NewNPP_ShutdownProc(NP_Shutdown);
}
return err;
}
#endif // XP_MACOSX
这三个平台接口实现的功能都一样,但firefox硬把他们写的这么复杂。仔细对比一下npruntime原来的接口就会明白这三个平台到底有什么不同,这里多出了一个main函数,这个main函数是在MAC OS X的firefox 2里面才有的,在firefox 3中已经取消了,但为了插件的可移植性还是把这个函数加了进来,里面调用了了其他的三个接口函数来实现,本质上没有任何区别。
这3个平台的声明plugin信息的方式也是不一样的。Windows的plugin信息都存放在资源的Version中,可以用VC或记事本打开进行编辑,Linux上的MIMEType需要通过导出函数NP_GetMIMEDescription来获取,而其他的信息则通过NPP_GetValue来获取,firefox通过这个函数向plugin请求插件名称和插件描述。在MAC OS X上plugin信息是放在资源脚本里,要先定义.r 文件,添加到XCode中,.r文件的一个范例如下
#include <CoreServices/CoreServices.r>
resource 'STR#' (127)
{{
"myplugins"
}};
resource 'STR#' (128)
{{
"application/myscriptable-plugin",
"*"
}};
resource 'STR#' (126)
{{
"<a href=\"http://www.mypage.com\">MyPlugin 1.0.0</a> "
"can do something",
"my discription。"
}};
然后将这个文件和源文件一起编译成一个Bundle(相当于一个DLL),在XCode中还需要将CFBundlePackageType改为BRPL。因为MAC OS X还可运行在ppc架构上,所以最好能编写成universal版,这样就不用考虑平台问题了。
至此,这个接口就算完成了,能够在三个平台上工作。