Posted on 2009-05-18 15:59
eyesmart 閱讀(5785)
評(píng)論(2) 編輯 收藏 引用 所屬分類:
Basic Knowledge
最近開發(fā)了一個(gè)firefox plugin,有點(diǎn)心得,決定將其寫下來。因?yàn)閜lugin的一些規(guī)則比較死板,所以了解其中函數(shù)的關(guān)系和調(diào)用的先后順序還要參考https://developer.mozilla.org/en/Plugins,本文主要討論Windows,Linux,MAC OS X三個(gè)平臺(tái)間的差異以及安裝更新的過程。
plugin其實(shí)是一個(gè)可執(zhí)行的文件(動(dòng)態(tài)庫),主要用于擴(kuò)充瀏覽器對(duì)數(shù)據(jù)的解釋能力,如flash,pdf格式,也可以用于與本機(jī)其他模塊或?qū)ο筮M(jìn)行通信,如具有腳本訪問功能的plugin。因?yàn)槲蚁腴_發(fā)一個(gè)具有腳本訪問功能的plugin,所以從firefox源代碼包中我選擇了npruntime,因?yàn)檫@些結(jié)構(gòu)都是比較固定的。
構(gòu)建npruntime之前要安裝SDK,新建工程或makefile,設(shè)置一些宏定義,如XP_WIN=1,這個(gè)是很容易成功的。要是npruntime跨平臺(tái)還需要修改其代碼,經(jīng)多次摸索以及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


這三個(gè)平臺(tái)接口實(shí)現(xiàn)的功能都一樣,但firefox硬把他們寫的這么復(fù)雜。仔細(xì)對(duì)比一下npruntime原來的接口就會(huì)明白這三個(gè)平臺(tái)到底有什么不同,這里多出了一個(gè)main函數(shù),這個(gè)main函數(shù)是在MAC OS X的firefox 2里面才有的,在firefox 3中已經(jīng)取消了,但為了插件的可移植性還是把這個(gè)函數(shù)加了進(jìn)來,里面調(diào)用了了其他的三個(gè)接口函數(shù)來實(shí)現(xiàn),本質(zhì)上沒有任何區(qū)別。
這3個(gè)平臺(tái)的聲明plugin信息的方式也是不一樣的。Windows的plugin信息都存放在資源的Version中,可以用VC或記事本打開進(jìn)行編輯,Linux上的MIMEType需要通過導(dǎo)出函數(shù)NP_GetMIMEDescription來獲取,而其他的信息則通過NPP_GetValue來獲取,firefox通過這個(gè)函數(shù)向plugin請(qǐng)求插件名稱和插件描述。在MAC OS X上plugin信息是放在資源腳本里,要先定義.r 文件,添加到XCode中,.r文件的一個(gè)范例如下
#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。"
}};
然后將這個(gè)文件和源文件一起編譯成一個(gè)Bundle(相當(dāng)于一個(gè)DLL),在XCode中還需要將CFBundlePackageType改為BRPL。因?yàn)镸AC OS X還可運(yùn)行在ppc架構(gòu)上,所以最好能編寫成universal版,這樣就不用考慮平臺(tái)問題了。
至此,這個(gè)接口就算完成了,能夠在三個(gè)平臺(tái)上工作。