Firefox插件(plugins)开发实用指南

Firefox插件可实现强大功能,但其中麻烦事情不少。写这个实用指南首先是为了方便自己记忆,免得以后再次栽倒一些坑里面,如果能帮助其他人,则是更好。这个指南不是为了手把手教读者开发插件,而是作为一个FAQ,解决各种诡异问题

Firefox拥有众多的扩展(Extension),开发扩展也非常容易,不过有一些事情还是无法用扩展解决,需要访问操作系统的底层功能,这就需要写插件(plugins)。例如flash就是一个插件而不是扩展。

Mozilla提供了一系列的教程和文档,虽然很不详尽,众多重要的API语焉不详,但至少是一个好的开始。

最需要阅读的是plugins API和使用入门。这是一个相当长的文档,如果看完所有的内容会花费大量的时间而且还会很晕,这里列一些重点供参考。

完成以上这些内容以后差不多就已经可以实现自己的插件了,一般而言,参照着例子来做开发不会有什么问题,只是有不少细节需要留意。

Firefox plugins开发的众多奇怪的约定(假设plugins已经被正确安装)

有些约定非常奇怪,不要问我为什么,天晓得开发firefox的牛人们怎么想的。

在Windows下,plugins必须满足以下条件才能被firefox检测到:

在Linux下,plugins必须满足以下条件才能被检测到:

特别需要说明的是,NP_GetPluginVersion、NP_GetEntryPoints等关键函数没有任何官方文档介绍它们,只能根据例子来猜,反正没事就别改它们的实现,copy例子中的代码就好。

firefox插件开发注意事项

写firefox插件的一个基本习惯是,经常编译代码并运行它,保证你的插件还能工作。只要firefox无法加载dll/so,或者加载出现任何错误,都会悄无声息的忽略这个插件。时常关注一下about:plugins,看看插件是不是还在这个列表里。

firefox插件从窗口模式上可分为windowlesswindowed两种。其中,windowless模式的文档较多较全,是firefox比较推荐的模式,坑比较少,这里就不多说了。windowed模式则相反,需要好好说说。

无论在Windows还是Linux上,windowed的插件都拥有独立于浏览器页面的窗口。firefox会通过插件的NPP_SetWindow来告诉插件当前窗口的情况。

关于windowed插件有两个诡异问题需要注意:

测试firefox插件小技巧,测试方面的高手可以无视

测试插件前建议先在firefox里面创建一个新的profile(帐号)。这样可以创造一个最干净的开发环境,避免被其他扩展/插件干扰。

默认的profile名叫default,在命令行里输入firefox -p default就可以使用这个profile。如果只是输入firefox -p,会弹出一个对话框用于选择profile。这个命令在Windows和Linux下都可使用。

无论是哪个平台,调试插件的方法都很类似。

Windows下可以用VC以调试方式启动firefox,载入插件时调试器会自动载入对应的符号,捕捉发生的异常或者设断点都很方便。

Linux下直接用gdb就好,细节应该不用多说。有一点需要注意,系统默认安装的firefox命令(默认放在/usr/bin/firefox)是一个shell脚本,真正的可执行文件名字需要打开这个脚本自行查找。

实现firefox插件的基本功能

firefox为插件提供的接口十分原始,很多功能默认没有实现,下面提供了一些思路和方法。

void FireHTMLEvent(NPP npp, const string & name)
{
    NPVariant result;
    NPObject *window;
    NPVariant vDoc;

    NPN_GetValue(npp, NPNVWindowNPObject, &window);

    // 也许页面已经跳转了……
    if (!window) {
        return;
    }

    NPIdentifier sDocument = NPN_GetStringIdentifier("document");
    NPN_GetProperty(npp, window, sDocument, &vDoc);
    NPN_ReleaseObject(window);

    // evt = document.createEvent("KeyboardEvent");
    NPVariant evt;
    NPObject* npDoc = NPVARIANT_TO_OBJECT(vDoc);
    NPIdentifier createEvent = NPN_GetStringIdentifier("createEvent");
    NPVariant eventArgs[1];
    STRINGZ_TO_NPVARIANT("HTMLEvents", eventArgs[0]);
    NPN_Invoke(npp, npDoc, createEvent, eventArgs, 1, &evt);
    NPN_ReleaseObject(npDoc);

    // evt.initKeyEvent(
    //    "blur",      //  in DOMString typeArg,
    //    false,            //  in boolean canBubbleArg,
    //    false);            //  in boolean cancelableArg,
    NPObject * npEvt = NPVARIANT_TO_OBJECT(evt);
    NPIdentifier initKeyEvent = NPN_GetStringIdentifier("initEvent");
    NPVariant initArgs[3];
    STRINGZ_TO_NPVARIANT(name.c_str(), initArgs[0]);
    BOOLEAN_TO_NPVARIANT(false, initArgs[1]);
    BOOLEAN_TO_NPVARIANT(false, initArgs[2]);
    NPN_Invoke(npp, npEvt, initKeyEvent, initArgs, 3, &result);
    NPN_ReleaseVariantValue(&result);

    // this.dispatchEvent(evt);
    NPObject * self;
    NPN_GetValue(npp, NPNVPluginElementNPObject, &self);
    NPIdentifier dispatchEvent = NPN_GetStringIdentifier("dispatchEvent");
    NPVariant dispatchArgs[1];
    dispatchArgs[0] = evt;
    NPN_Invoke(npp, self, dispatchEvent, dispatchArgs, 1, &result);
    NPN_ReleaseVariantValue(&result);
    NPN_ReleaseObject(npEvt);

    NPN_ReleaseObject(self);
}

暂时先写到这里,如果还有什么疑难杂症,欢迎评论,我继续添加。

相关阅读

有话想说?请留下评论吧~~如果喜欢我的blog,欢迎订阅~~

评论

在linux下如何单独编译plugin呢?

例如,在npruntime这个plugin上做了修改,如何单独编译它,而不是整个firefox。

新手上路,请多指教.

[回复]

留下评论

(必需)

(必需)