先說一句題外話,上一節(jié)一開始我提到了由于.rc導致我自己寫的插件不能被FF識別的問題,今天終于找到答案了。在這篇文章中:https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Plug-in_Development_Overview,有這么一段話:In your development environment, make sure your language is set to "US English" and the character set to "Windows Multilingual." The resource code for this language and character set combination is 040904E4. 看來這FF還只能使用英文。
OK,步入正題。這一小節(jié)我來簡單說一下如何為插件添加和腳本語言(如Javascript)交互的能力。我會為插件添加幾個函數,供Javascript調用。
前面我們提到過瀏覽器調用插件的方法的順序,基本上為:NP_GetEntryPoints、NP_Initialize、NPP_New、NPP_SetWindow、NPP_GetValue。在NPP_New中,我們需要創(chuàng)建插件對象的實例,NPP_SetWindow中,瀏覽器會傳入插件窗口的信息,最后一個NPP_GetValue,是瀏覽器來獲取一些插件信息的。NPP_GetValue函數的結構是這樣的:
NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
- instance包含著插件對象實例;
- variable表示瀏覽器要獲取的信息的類型;
- value表示返回給瀏覽器的值
瀏覽器會傳入NPPVpluginScriptableNPObject(作為variable參數)來查詢插件是否支持Scriptable功能(即和腳本語言交互的功能),在這里,我們可以利用NPN_CreateObject方法來創(chuàng)建一個NPObject對象,并且作為value返回給瀏覽器。這樣,瀏覽器就通過這個NPObject對象和我們的插件建立了連接。當頁面上Javascript調用了我們插件對象的某個方法時,瀏覽器會調用該NPObject對象的HasMethod方法來查詢是否支持這個方法,如果支持,則會調用NPObject對象的Invoke方法,傳入方法名、參數等信息。這樣,我們就可以讓網頁上的腳本語言來執(zhí)行我們編寫的函數了。在Windows上,我們編寫的函數就如同編寫普通的應用程序一樣,可以使用很多Windows API來完成許多復雜的工作。
上面有個問題:如何創(chuàng)建我們自己的NPObject對象?NPN_CreateObject方法如何使用?好在Mozilla給我們提供了npruntime這個例子程序,可以讓我們得以參考。
先來看看NPN_CreateObject方法的定義:
NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);
關鍵在第二個參數上,我們需要提供一個NPClass指針。npruntime例子程序中是這么做的:
定義了一個宏DECLARE_NPOBJECT_CLASS_WITH_BASE,其作用就是定義了一個靜態(tài)的NPClass對象,并且NPClass要求的所有基礎方法,都由一個ScriptablePluginObjectBase類來提供。我們根據需要,來創(chuàng)建不同的繼承于ScriptablePluginObjectBase的類(比如支持方法的類和支持屬性的類),傳給DECLARE_NPOBJECT_CLASS_WITH_BASE宏,這樣,當瀏覽器管我們“要”的時候,我們就可以按照它的需要“給”它對應的對象。
npruntime例子中,ScriptablePluginObject是用來處理方法的,而ConstructablePluginObject是用來處理屬性的。
如何定義一個方法(或屬性)?
1、添加一個方法(或屬性)很簡單,先定義一個靜態(tài)NPIdentifier類型的變量,例如:
static NPIdentifier s_idSetArgs;
2、在插件對象構造函數中,使用NPN_GetStringIdentifier方法來設置該方法的名稱,例如:
s_idSetArgs = NPN_GetStringIdentifier("SetArgs");
其中,SetArgs就是我們提供給腳本語言調用的方法名稱。
3、在ScriptablePluginObject的HasMethod方法中,判斷傳入的方法名:
bool ScriptablePluginObject::HasMethod(NPIdentifier name)
{
if(name == s_idSetArgs)
{
printf("method name = SetArgs\n");
return true;
}
return false;
}
4、在ScriptablePluginObject的Invoke方法中,判斷如果傳入的方法名稱等于我們定義的方法名,則做你想要做得事情:
//////////////////////////////////////////////////////////////////////////
///
/// @brief 如果某個方法支持(使用HasMethod檢測),當頁面上Javascript代碼調用該方法時,會執(zhí)行本函數
///
/// @param [in] name 方法名
/// @param [in] args 參數值(數組)
/// @param [in] argCount 參數個數
/// @param [in] result 執(zhí)行后返回給調用者的結果
///
/// @return PR_TRUE表示執(zhí)行成功,PR_FALSE表示失敗
///
//////////////////////////////////////////////////////////////////////////
bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
if(name == s_idSetArgs)
{
這里做你想要做得事情
return PR_TRUE;
}
return PR_FALSE;
}
關于方法參數的接收,這里舉個例子。比如網頁上這么調用:
embedobj.SetArgs("name", "value");
在我們的方法中,就可以這么接收:
if(args != NULL && argCount >= 2)
{
NPVariant npvName = args[0]; //第一個參數
NPVariant npvValue = args[1]; //第二個參數
if(NPVARIANT_IS_STRING(npvName) && NPVARIANT_IS_STRING(npvValue)) //如果兩者都是字符串類型(當然你還可以判斷是否是其他類型)
{
NPString npsName = NPVARIANT_TO_STRING(npvName); //轉成NPString
NPString npsValue = NPVARIANT_TO_STRING(npvValue);
if(npsName.utf8characters && strlen(npsName.utf8characters) > 0) //限定條件,可以根據需要進行修改。這里限定第一個參數內容不能為空
{
int nLenName = strlen(npsName.utf8characters) + 1;
int nLenValue = strlen(npsValue.utf8characters) + 1;
PARAMPAIR paramPair;
paramPair.pName = new char[nLenName];
memset(paramPair.pName, 0, nLenName);
paramPair.pValue = new char[nLenValue];
memset(paramPair.pValue, 0, nLenValue);
strcpy(paramPair.pName, npsName.utf8characters); //將參數內存存儲到我們熟悉的C
strcpy(paramPair.pValue, npsValue.utf8characters);
m_vecParamPair.push_back(paramPair);
}
}
}
上面的代碼中,PARAMPAIR就是一個簡單的結構體:
typedef struct tagPARAMPAIR
{
LPTSTR pName;
LPTSTR pValue;
}PARAMPAIR, *PPARAMPAIR;
m_vecParamPair是一個vector:vector<PARAMPAIR> m_vecParamPair;
順便說一句,上面只是代碼片段,關于內存釋放、vector清空等操作,由于不是這里要說的關鍵部分,所以沒有列出。
OK,現在我們的插件已經可以順利和網頁進行交互工作了。