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,现在我们的插件已经可以顺利和网页进行交互工作了。