我的玻璃盒子

【原创】我的Firefox插件开发之旅(7)——为插件添加和脚本交互的能力

先说一句题外话,上一节一开始我提到了由于.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,步入正题。这一小节我来简单说一下如何为插件添加和脚本语言(如Javascript)交互的能力。我会为插件添加几个函数,供Javascript调用。

前面我们提到过浏览器调用插件的方法的顺序,基本上为: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,现在我们的插件已经可以顺利和网页进行交互工作了。

posted on 2008-11-14 10:56 深蓝色系统 阅读(16077) 评论(10)  编辑 收藏 引用 所属分类: Firefox开发

评论

# re: 我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2009-07-29 13:37 tmy13

学习了下npapi再回过头来看楼主的这篇文章,受益匪浅  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2009-10-19 15:43 tt

我是一个新手,能不能做一个 hello word 的例子?  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2009-11-11 11:28 Pat

太好了。高手。我看这代码都看不明白,现在懂个大概了,非常感谢!  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2009-11-11 18:31 Pat

你好,我想知道怎么样从plugin里面访问JS,能留个联系方式吗?QQ  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力[未登录] 2010-11-02 15:27 Eping

在资源文件里添加版本信息,保证 BLOCK "040904e4" 即可,是否为英语倒不是这么重要,查看Firefox源代码可发现,这才是最重要的!
if(::GetFileVersionInfo((char*)path, NULL, versionsize, verbuf))
{
info.fName = GetKeyValue(verbuf, "\\StringFileInfo\\040904E4\\ProductName");
info.fDescription = GetKeyValue(verbuf, "\\StringFileInfo\\040904E4\\FileDescription");

char *mimeType = GetKeyValue(verbuf, "\\StringFileInfo\\040904E4\\MIMEType");
char *mimeDescription = GetKeyValue(verbuf, "\\StringFileInfo\\040904E4\\FileOpenName");
char *extensions = GetKeyValue(verbuf, "\\StringFileInfo\\040904E4\\FileExtents");

BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", " \0"
VALUE "FileDescription", "Adobe Flash movie \0"
VALUE "FileExtents", "swf\0"  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2011-08-26 15:14 求稳

你好,我想请问下, 能通过firefox extension 来 调用 NPAPI dll写的方法吗? 如果可以,该通过什么方式呢? xul 里 还是 js 里呢?  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2012-07-06 18:10 陈后根

可以这里没有讲如何用插件调用js中的函数,应该怎样实现这个功能呢?
  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2013-09-13 14:57 maqingfeng

你好,我想问一下,如何把接收到的参数,传递给插件对象呢?就是在pluging.cpp中有一个回调函数,在这上面画出传进来的字符串。而这个函数不是plugin的成员函数。  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力[未登录] 2013-09-18 10:11 sun

大神,我想做firefox plugin 加密,能不能给个demo看看【15527974275@163.com】,我都不知道
bool ScriptablePluginObject::HasMethod(NPIdentifier name) ,这个方法放在哪里? 我以前是搞object_c的,,这个完全不懂,研究半个月,卡在这个地方了,seek help!  回复  更多评论   

# re: 【原创】我的Firefox插件开发之旅(7)&mdash;&mdash;为插件添加和脚本交互的能力 2016-03-05 01:26 zzx

报错:无法解析外部符号_NPN_CreateObject,很纳闷啊!  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2008年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

统计

常用链接

留言簿(75)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜