火狐插件開發教程


firefox的插件分兩種類型,一種extension,叫擴展,一種是plugin,我們叫插件.兩種是完全不同的兩個東西。extension相對來說簡單很多,用的主要是XUL,只是xml的一個變相。而plugin相對來說復雜一些。具體的官網說明如下https://developer.mozilla.org/en/Gecko_Plugin_API_Reference
一、 插件的標准
  1、在windows平台,火狐的插件是以動態庫形式(dll)存在的,並只去識別在其安裝目錄下plugins文件夾下的dll;
  2、dll的名是以8.3原則來命名的,即其名字所包含的字符不超過8個字符且以dll結尾的文件,還有一個規定就是其名字必須以np開頭,例如:npXXX.dll,而XXX不超過六個字符;
  3、dll是屬於MIME(一種標准)類型,要不火狐瀏覽器不認識他;
  4、dll導出的函數必須是NP_GetEntryPoints、NP_Initialize、NP_Shutdown是這三個,這是火狐瀏覽器能夠識別的在個接口,具體說明在插件的生命周期中說明。
二、 插件的生命周期
  1、 第一次打開含量有插件的頁面時,瀏覽器最先調用NP_GetEntryPoints作為調用插件的入口,此方法也只在第一次加載插件時調用。
  2、 調用NP_GetEntryPoints后,瀏覽器會調用NP_Initialize初始化插件,NP_Initialize只在第一次調用時被瀏覽器調 用,與NP_Initialize配對的是NP_Shutdown,NP_Shutdown是在關閉了所有含有該插件的頁面后被瀏覽器調用,在生命周期內 也只被調用一次
  3、 調用NP_Initialize后,瀏覽器會調會NPP_New來創建一個插件實例,每打開一個頁面都會調用NPP_New一次來創建一個插件實例,與 NPP_New配對的是NPP_Destory,在每關閉一個頁面都會調用NPP_Destory來釋放NPP_New創建的實例
  4、 調用NP_Initialize后,一般會調用NPP_SetWindow來調置窗口,對於沒有窗口的插件當然不用調用。
注:火狐插件開發的sdk划分為兩類接口,一類是與插件相關的,以NPP或者NP開頭的;一類是與瀏覽器相關的,以NPN開頭的。
三、 頁面調用插件的方式
 1、 通過object方式調用:與IE調用ActiveX控件的方式一樣的,不同的是IE調用ActiveX控件是通過ClassID來標識的,而火狐的控件是通過MIME的值來標識。
 2、 通過embed方式調用:與object方式類似,但操作方面簡單一些
四、 插件與頁面的信息傳遞
插件與頁面的信息是通過插件的接口與JS進行信息傳遞。在火狐插件有一類接口可以直接讀取頁面的JS函數,並從函數中獲取相應的值或者調置相關的值。
具體的例子有時間再加上,現在還在頭大中。。。。

 

三、火狐調用插件的過程

  NP_GetEntryPoints → NP_Initialize → NPP_New → NPP_SetWindow → NPP_GetValue

     在NPP_New中,我們需要創建插件對象的實例,NPP_SetWindow中,瀏覽器會傳入插件窗口的信息,最后一個NPP_GetValue,是瀏覽器來獲取一些插件信息的。

NPP_GetValue函數的結構:

  NPError  NPP_GetValue(NPP instance, NPPVariable variable, void *value);

  • instance包含着插件對象實例;
  • variable表示瀏覽器要獲取的信息的類型;
  • value表示返回給瀏覽器的值

瀏覽器會傳入NPPVpluginScriptableNPObject(作為variable參數)來查詢插件是否支 持 Scriptable功能(即和腳本語言交互的功能),在這里,我們可以利用NPN_CreateObject方法來創建一個NPObject對象,並且 作為value返回給瀏覽器。這樣,瀏覽器就通過這個NPObject對象和我們的插件建立了連接。當頁面上Javascript調用了我們插件對象的某 個方法時,瀏覽器會調用該NPObject對象的HasMethod方法來查詢是否支持這個方法,如果支持,則會調用NPObject對象的Invoke 方法,傳入方法名、參數等信息。這樣,我們就可以讓網頁上的腳本語言來執行我們編寫的函數了。在Windows上,我們編寫的函數就如同編寫普通的應用程 序一樣,可以使用很多Windows API來完成許多復雜的工作。

上面有個問題:如何創建我們自己的NPObject對象?NPN_CreateObject方法如何使用?好在Mozilla給我們提供了npruntime這個例子程序,可以讓我們得以參考。

先來看看NPN_CreateObject方法的定義:

NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);

關鍵在第二個參數上,我們需要提供一個NPClass指針。npruntime例子程序中是這么做的:

定 義了一個宏DECLARE_NPOBJECT_CLASS_WITH_BASE,其作用就是定義了一個靜態的NPClass對象,並且 NPClass要求的所有基礎方法,都由一個ScriptablePluginObjectBase類來提供。我們根據需要,來創建不同的繼承於 ScriptablePluginObjectBase的類(比如支持方法的類和支持屬性的類),傳給 DECLARE_NPOBJECT_CLASS_WITH_BASE宏,這樣,當瀏覽器管我們“要”的時候,我們就可以按照它的需要“給”它對應的對象。

npruntime例子中,ScriptablePluginObject是用來處理方法的,而ConstructablePluginObject是用來處理屬性的。

 

如何定義一個方法(或屬性)?

1、添加一個方法(或屬性)很簡單,先定義一個靜態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代碼調用該方法時,會執行本函數
///
/// @param [in] name    方法名
/// @param [in] args    參數值(數組)
/// @param [in] argCount    參數個數
/// @param [in] result    執行后返回給調用者的結果
///
/// @return PR_TRUE表示執行成功,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,現在我們的插件已經可以順利和網頁進行交互工作了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM