使用NPAPI开发Fireforx/Chrome插件

一、编译runtime

 1、Download SDK:
   ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/4.0.1/source/ 下载解压firefox-4.0.1.source.tar.bz2文件
   解压的文件夹firefox-4.0.1.source\mozilla-2.0\modules\plugin,里面有开发NPAPI插件所需的所有资源.
   
2、在plugin目录下面的sdk\samples可以找到npruntime的例子,接着根据文档说明,一步一步建立vs2008工程
   
   我的路径是桌面下c:\Users\Administrator\Desktop\firefox-4.0.1.source\mozilla-2.0\modules\plugin\sdk\samples\npruntime

   首先:  使用VS2008创建空的win32 dll项目,然后设置工程属性:
   步骤一:添加npruntime文件夹下面所有的.h,.cpp文件,还有.rc,.def文件
   步骤二:工程设置库路径:  工程属性->c/c++常规:附加包含目录   ../../include;../../../../base/public
   步骤三:工程设置预处理器:工程属性->c/c++->预处理器:预处理器定义:WIN32;_DEBUG;_WINDOWS;_USRDLL;NPRUNTIME_EXPORTS;XP_WIN32;XP_WIN;_X86_
   步骤四:工程设置预编译头:工程属性->c/c++->预编译头:创建/使用预编译头:不使用预编译头
   步骤五:建立导出文件声明:def,指定导出接口
   步骤六:将.rc文件里面的FileDescrip,fileOpenName,internalName,OrigianalName都改成与工程统一的名称
   
   开始编译:一堆错误,主要是一下几个
   error C3861: 'printf': identifier not found 
   error C2664: 'DrawTextW' : cannot convert parameter 2 from 'char [128]' to 'LPCWSTR'
   error C2275: 'NPPluginFuncs' : illegal use of this type as an expression
   error C2065: 'setvalue' : undeclared identifier
   error C3861: 'offsetof': identifier not found
   第一个错误:plugin.cpp 不认识printf,由于没使用预编译头,因此加上#include <stdio.h>
   第二个错误:编译使用的是unicode字符集,因此DrawText会被按照DrawTextW来编译,可以修改工程属性,使用多字符集编译或者将DrawText改为DrawTextA
   第三、四五个错误:setvalue offsetoff缺少,加上#include <stddef.h>
   
   再重新编译,ok,看到了激动的npruntime.dll

   参考:https://developer.mozilla.org/en-US/docs/Compiling_The_npruntime_Sample_Plugin_in_Visual_Studio


3、检测fireforx是否识别
   将npruntime.dll拷贝到fireforx下的plugins目录下:没装插件貌似没有这个目录,自己新建一个,放进去,打开fireforx,输入about:plugins,看到了  
    
    npruntime scriptable example plugin

    文件: npruntime.dll
    版本: 1.0.0.1
    npruntime
    MIME 类型                                          描述          后缀
    application/mozilla-npruntime-scriptable-plugin  npruntime  rts
    成功。


4、用fireforx打开npruntime目录下面的test.html目录,奇怪,没加载,打开test.html查看了一下,原来有个地方需要改为:<embed type="application/mozilla-

npruntime-scriptable-plugin" style="display: block; width: 50%; height: 100px;"><br>
 插件的MIME 类型是application/mozilla-npruntime-scriptable-plugin,fireforx是根据MEMETYPE来加载的。因此需要改成runtime的mime
 再次打开,ok,可以点击页面几个按钮感受一下交互

 例子编译完成,有了初步的认识之后,接下来需要熟悉一下code了。

二、建立自己的插件

1、VS2008建立空的dll,工程名称:npdemo
2、将npruntime下的相关.h,.cpp,.def拷贝过来,加入到工程中
3、加入头文件,npruntime同级目录下(firefox-4.0.1.sourc\mozilla-2.0\modules\plugin)include整个文件夹(包含2个.h文件)拷贝到工程目录下面,同时将目录

(firefox-4.0.1.source\mozilla-2.0\modules\plugin\base\public下所有文件拷入到刚才的拷入工程的inlcude的里面
   此处就是将需要包含的目录独立到工程目录下来

4、对工程进行设置并且进行编译
   参照编译runtime。https://developer.mozilla.org/en-US/docs/Compiling_The_npruntime_Sample_Plugin_in_Visual_Studio
   需要注意的是: 目录包含先前拷过来的include就够了,以及编译选项和 resource的version上。当然重点还是version上面
      新建resource->version文件:声明dll版本信息。编辑version,在资源文件npdemo.rc右键查看代码,进入编辑Version段
      添加VALUE "MIMEType", "application/mozilla-npdemo-plugin",此id是Fireforx识别插件的标识
          VALUE "FileExtents", "rts"
       保存,关闭。在打开,就会看见了。  
   此处注意:
   .rc文件必须是英文(美国)编码040904E4,打开version
    同时选中:Block Header,进行属性编辑。(不是version node的attribute)
                        Language:英文(美国)
                       Code Page:Windows 3.1 拉丁语 1 (美国、西欧)版

VALUE "Translation", 0x409, 1252

文档说明:https://developer.mozilla.org/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Development_Overview

5、编译ok,Fireforx也能发现,编译部分完工,接下来处理与js交互方面。详见npdemo

A:js主动调用插件接口。js进行插件调用接口传递的时候,是通过接口id名称来进行传递的,NPAPI里面的NPIdentifier。

      以test接口js调用为例:

      js 方面:   

[javascript]  view plain copy
  1. var rtn = embed1.Test(true,456,7777,"abssssss","abcsssss\0");   //函数名称Test 插件提供  


      首先:是先声明一个全局的接口id,

[cpp]  view plain copy
  1. static NPIdentifier s_idTest;  

      然后:在plugin类初始化的时候,通过接口名称获取接口的id:

[cpp]  view plain copy
  1. s_idTest = NPN_GetStringIdentifier("Test");  


      其次:在ScriptablePluginObject::HasMethod(NPIdentifier name)里面需要指明由此方法,浏览器调用的时候,会先进入到此查询是否有此方法,然后再进入    ScriptablePluginObject::Invoke去调用接口方法。

[cpp]  view plain copy
  1. if(name == s_idTest || name == s_idTest1)  
  2.  {  
  3.   OutputDebugStringA("has method Test test1");  
  4.   return true;  
  5.  }  

再次:在ScriptablePluginObject::Invoke里面进行接口的实现,根据接口名称判断是否是此接口来进行对接口的实现过程
 

[cpp]  view plain copy
  1. if(name == s_idTest)  
  2.   {  
  3.    OutputDebugStringA("Test called\n");  
  4.    if(args != NULL && argCount >= 5)  
  5.    {  
  6.     //fun paramer  
  7.     NPVariant npParam1   = args[0];  
  8.     NPVariant npParam2   = args[1];  
  9.     NPVariant npParam3   = args[2];  
  10.     NPVariant npParam4   = args[3];  
  11.     NPVariant npParam5   = args[4];  
  12.     bool npbParam1 = NPVARIANT_TO_BOOLEAN(npParam1);  
  13.     //此处传入2个int类型参数,  
  14.     //Fireforx下:npnParam2正常 npnParam3为0  
  15.     //Chrome  下:npnParam2为0  npnParam3正常  
  16.     //即:传入的int参数 Fireforx下需要从int32取,chrome下需要从double下取  
  17.     int32_t  npnParam2 = NPVARIANT_TO_INT32(npParam2);  
  18.     int32_t  npnParam3 = NPVARIANT_TO_DOUBLE(npParam3);  
  19.     //传入两个string  npsParam5后面增加\0,npsParam4后面不加  
  20.     //Fireforx下:都正常  
  21.     //chrome  下:加了\0的正常,不加的会有乱码出现  
  22.     NPString npsParam4 = NPVARIANT_TO_STRING(npParam4);  
  23.     NPString npsParam5 = NPVARIANT_TO_STRING(npParam5);  
  24.     //构造string返回值 result  
  25.     char *temp = (char *)NPN_MemAlloc(1024);  
  26.     if(temp == NULL)  
  27.      return false;  
  28.     sprintf(temp,"npdemodll:bool:[%d] int[%d] int:[%d] string:[%s] string:[%s]",  
  29.      npbParam1,npnParam2,npnParam3,npsParam4.UTF8Characters,npsParam5.UTF8Characters);  
  30.     STRINGZ_TO_NPVARIANT(temp,*result);  
  31.     //return value  
  32.     //DOUBLE_TO_NPVARIANT(2437309816,*result);  
  33.     //BOOL bResult  = FALSE;  
  34.     //BOOLEAN_TO_NPVARIANT(bResult,*result);  
  35.   
  36.     return true;  
  37.    }  
  38.   }  

B:js提供接口给插件调用,即回调。

      以jsCallbackFun为例

     js方面:

[javascript]  view plain copy
  1. embed1.jsCallbackFun = jsfunc;    //embed1为插件对象,jsCallbackFun回调接口做为插件对象的一个属性,被赋值为js的函数,有插件提供  
  2.   
  3.   
  4.      function jsfunc(state)  
  5.     {  
  6.          alert("jsfunc called!");  
  7.          alert(state);  
  8.     }  


 

     插件方面:

     同样声明回调接口id:         

[cpp]  view plain copy
  1. static NPIdentifier s_idjsCallbackFun;  

     同样获取接口名称 :         

[cpp]  view plain copy
  1. s_idjsCallbackFun = NPN_GetStringIdentifier("jsCallbackFun");  


     不同是:此接口作为属性提供,因此在ScriptablePluginObject::HasProperty需要声明

[cpp]  view plain copy
  1. bool  ScriptablePluginObject::HasProperty(NPIdentifier name)  
  2.   {  
  3.        return name == s_idjsCallbackFun;  
  4.        //return false;  
  5.   }  

 同时也需要在属性设置 ScriptablePluginObject::SetProperty下设置。

 需要先在cplugin类下增加一个回调接口对象的成员变量:NPObject *m_pJSCallbackFunObj;,同时提供访问和设置属性接口,m_pJSCallbackFunObj 需要构造为null,不然会崩溃

[cpp]  view plain copy
  1. bool   ScriptablePluginObject::SetProperty(NPIdentifier name,  
  2.          const NPVariant *value)  
  3. {  
  4.  //js property  
  5.  if(name == s_idjsCallbackFun)  
  6.  {  
  7.   CPlugin *plugin = (CPlugin *)mNpp->pdata;  
  8.   if(plugin == NULL)  
  9.    return false;  
  10.   if(plugin->GetJSCallbackFunObj() == NULL)  
  11.   {  
  12.    //set js property obj  
  13.    NPObject *pObject = NPN_RetainObject(NPVARIANT_TO_OBJECT(*value));  
  14.    plugin->SetJSCallbackFunObj(pObject);  
  15.   }  
  16.   return true;  
  17.  }  
  18.  return false;  
  19. }  

然后便可以使用m_pJSCallbackFunObj作为NPN_InvokeDefault的参数进行回调js的函数了,封装了一下调用过程为cplug的成员函数,可以在需要回调的时候调用

[cpp]  view plain copy
  1. bool CPlugin::JSCallbackFun(int32_t nState)  
  2. {  
  3.  if (m_pJSCallbackFunObj != NULL)  
  4.  {  
  5.   NPVariant result;   
  6.   NPVariant relements[1];  
  7.   INT32_TO_NPVARIANT(nState,relements[0]);  
  8.   //invoke js fun by js obj  
  9.   NPN_InvokeDefault(m_pNPInstance,m_pJSCallbackFunObj,relements,1,&result);  
  10.   NPN_ReleaseVariantValue(&result);  
  11.  }  
  12.  return true;  
  13. }  

这段也可以直接放在test函数里面测试回调,nodemo里面增加了一个Test1接测试回调 

 下载源码

关于安装:自己提供安装包,让用户一键安装之后各个浏览器都能识别并使用,同时还要顾及到先安装插件后安装浏览器的问题,因此采用的是写注册表的方式,即使后安装浏览器,也能发现

详细参照:https://developer.mozilla.org/en-US/docs/Plugins/The_First_Install_Problem?redirectlocale=en-US&redirectslug=Plugins%3A_The_First_Install_Problem

这里碰到的问题记录一下,尽管解决了,可依旧有点懵:

1:在修改runtime的时候,碰到过Fireforx下面可以正常进行调用,但是在chrome下面,about:plugins可以出现,但是调用不了的情况,后来反复折腾,文件对比依旧没有发现哪里出问题,只得小心重来,后面居然好了,Fireforx群里面也有兄弟碰见类似问题,后来也不知道怎么样了,回头再研究下

2:js调用接口传递整数的参数,Fireforx下会存放在NPVariant下的int中,chrome下面会存放在double中,其它的还未测试过,这个问题也是排查了很久,Fireforx下好了之后转到在chrom下面,传递的int全都是0,根本就没有值,后来逐步排查,打印出传递进来的数据类型,才找到数据被放到了doubled段下面,至于原因,依旧困惑,后来索性直接全部改成string

3:编码问题,不知道是不是,刚开始在这方面找了不少原因:

      js传递字符串参数时:Fireforx下面一切正常,到chrome下面去,会有乱码现象,找了好久原因,各种编码都尝试过,自己手动强制转也不行,google干嘛呢这是,最后都差点要放弃了,偶然在网上看到个js字符串加上\0的,尝试一下,去,居然好了,在回到Fireforx下面,也好了,依旧不知为什么,无语了。。。。。。

后话:经过一段时间的奋战,初步完工,中间经历了不少困难,中文资料也比较少,官方文档英文一点点的看,遇到个一些的问题在来回折腾反复尝试之后,每每处在就要崩溃的边缘的时候,迎来了柳岸花开。此时先前所有的疲惫烦恼一扫而空,一下子又满怀斗志,继续前行,这种感觉相信做过coder的兄弟们你们都懂的。要不然怎会一头扎进茫茫的code之中呢。。。。。。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值