Posted on 2009-05-18 15:59
eyesmart 閱讀(5784)
評論(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版,這樣就不用考慮平臺問題了。
至此,這個接口就算完成了,能夠在三個平臺上工作。