NPAPI插件编程起步

 

http://geeklu.com/2010/10/getting-started-with-npapi-plugin/

 

2012-10-10 更新:https://code.google.com/p/chromium/issues/detail?id=139816 Chrome22之后貌似完全放弃10.5 Carbon之类的支持,所以Event Model需要进行设置,否则NPAPI插件在Chrome22下无法加载。NPP_New函数中进行如此设置

    browser->setvalue(instance, 

    NPPVpluginEventModel, 

    (void *)NPEventModelCocoa);


如果想学习NPAPI方面的知识,Mozila的官方wiki无疑是一个最佳的入口,https://wiki.mozilla.org/NPAPI 
FireBreath则可以作为实践的开端,https://github.com/firebreath/FireBreath 
融会贯通后便可以尝试从零开始写自己的插件了。


最近需要写一个Mac平台上的简单的跨浏览器的插件,需要在js中调用本地方法,而npapi满足此要求。 NPAPI就是Netscape Plugin Application Programming Interface的缩写了,虽然Netscape已经去了,但是这个却被沿用下来,在各大浏览器中都得以实现。还是纪念下曾经的浏览器的鼻祖啊。

在网络上搜索了很长时间,一直没有找到合适的满足自己需求的代码例子。且这方面的文档也少的可怜。 还是先提一下,有两个系列的文章还是不错,虽然或许可能也不完全正确,但是帮助理解npapi的编程模型是非常有帮助的:

http://colonelpanic.net/category/plugindev/npapi-plugindev/ http://rintarou.dyndns.org/category/browser/plugin

下面是我寻找解决方法的过程 一开始找到mozilla的一篇官方文档:Writing a plugin for Mac OS X,这里面提到了了官方的代码库中有demo,于是就跑到公司checkout下mozilla的分支。并看了下里面的一个mac的例子. 不过这个例子没有使用npruntime的东西,加上一开始的时候对npapi不熟悉,就继续搜索. 在google, stackoverflow,github, google codes 中不停的搜索,结果甚少,最后找到一个npruntime的例子,Call Native API from Google Chrome Extension on Mac OS X只可惜编译后只能在mac的chrome下运行。 后来又找到了Firebreath这个跨平台的浏览器NPAPI插件开发框架,可以通过python和cmake的配合,生成适合不同操作系统的浏览器插件的工程,于是就测试了,果真生成xcode项目后,编译,然后测试了下js调用插件的方法,还真能在各个浏览器(测试了三个比较常用的safari,chrome,firefox)下运行。但是无奈由于包含一些高级的程序代码其最小编译后的大小都2m+对于一个功能单一的插件来讲,无疑是让人无法接受的,所以继续找demo. 后来找到了npsimple-win32 这个windows下的vs的项目,我将其移植到mac上,编译运行后,唯独在safari下无法运行,悲剧的一塌糊涂,对于一个npapi不同的浏览器在实现的具体细节上竟然有细微的差别,真是太变态鸟。 最后无奈之下,将Weikit开源项目中的例子弄出来,然后和npruntime的相关代码进行整合,发现在safari,chrome可以运行在firefox中没法运行,好吧,至少现在两个例子的并集是完整的,我就将两个代码进行查看,最终找出了问题的所在,在safari下需要启动CoreGraphics,而在firefox下scriptable的NPObject的hasProperty和getProperty必须设置,可能在firefox下调用某函数,先去scriptable NPObject中找有没有这个名字的属性,然后在找方法吧。 最后终于写出了一个简单的例子

#import <WebKit/npapi.h>

#import <WebKit/npfunctions.h>

#import <WebKit/npruntime.h>







// Browser function table,可以通过它来得到浏览器提供的功能

static NPNetscapeFuncs* browser;

static const char *plugin_method_name_open = "open";



////////////////////////////////////

/*******各种接口的声明*********/

//在NPAPI编程的接口中你会发现有NP_打头的,有NPP_打头的,有NPN_打头的

//NP是npapi的插件库提供给浏览器的最上层的接口

//NPP即NP Plugin是插件本身提供给浏览器调用的接口,主要被用来填充NPPluginFuncs的结构体

//NPN即NP Netscape ,是浏览器提供给插件使用的接口,这些接口一般都在NPNetscapeFuncs结构体中



//Mach-o entry points,浏览器和创建交流的最上层的接口

NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);

NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);

void NP_Shutdown(void);



//NPP Functions

NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved);

NPError NPP_Destroy(NPP instance, NPSavedData** save);

NPError NPP_SetWindow(NPP instance, NPWindow* window);

NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype);

NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);

int32 NPP_WriteReady(NPP instance, NPStream* stream);

int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);

void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);

void NPP_Print(NPP instance, NPPrint* platformPrint);

int16_t NPP_HandleEvent(NPP instance, void* event);

void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData);

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

NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);



//Functions for scriptablePluginClass

bool plugin_has_method(NPObject *obj, NPIdentifier methodName);

bool plugin_invoke(NPObject *obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);

bool hasProperty(NPObject *obj, NPIdentifier propertyName);

bool getProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result);

////////////////////////////////////



static struct NPClass scriptablePluginClass = {

    NP_CLASS_STRUCT_VERSION,

    NULL,

    NULL,

    NULL,

    plugin_has_method,

    plugin_invoke,

    NULL,

    hasProperty,

    getProperty,

    NULL,

    NULL,

};



//接口的实现

NPError NP_Initialize(NPNetscapeFuncs* browserFuncs)

{

    browser = browserFuncs;

    return NPERR_NO_ERROR;

}



NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs)

{

    pluginFuncs->version = 11;

    pluginFuncs->size = sizeof(pluginFuncs);

    pluginFuncs->newp = NPP_New;

    pluginFuncs->destroy = NPP_Destroy;

    pluginFuncs->setwindow = NPP_SetWindow;

    pluginFuncs->newstream = NPP_NewStream;

    pluginFuncs->destroystream = NPP_DestroyStream;

    pluginFuncs->asfile = NPP_StreamAsFile;

    pluginFuncs->writeready = NPP_WriteReady;

    pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;

    pluginFuncs->print = NPP_Print;

    pluginFuncs->event = NPP_HandleEvent;

    pluginFuncs->urlnotify = NPP_URLNotify;

    pluginFuncs->getvalue = NPP_GetValue;

    pluginFuncs->setvalue = NPP_SetValue;

    

    return NPERR_NO_ERROR;

}





void NP_Shutdown(void)

{

    

}







bool plugin_has_method(NPObject *obj, NPIdentifier methodName) {

    // This function will be called when we invoke method on this plugin elements.

    NPUTF8 *name = browser->utf8fromidentifier(methodName);

    bool result = strcmp(name, plugin_method_name_open) == 0;

    browser->memfree(name);

    return result;

}

bool plugin_invoke(NPObject *obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result) {

    // Make sure the method called is "open".

    NPUTF8 *name = browser->utf8fromidentifier(methodName);

    if(strcmp(name, plugin_method_name_open) == 0) {

        browser->memfree(name);

        BOOLEAN_TO_NPVARIANT(false, *result);

        // Meke sure the arugment has at least one String parameter.

        if(argCount > 0 && NPVARIANT_IS_STRING(args[0])) {

            // Build CFURL object from the arugment.

            NPString str = NPVARIANT_TO_STRING(args[0]);

            CFURLRef url = CFURLCreateWithBytes(NULL, (const UInt8 *)str.UTF8Characters, str.UTF8Length, kCFStringEncodingUTF8, NULL);

            if(url) {

                // Open URL with the default application by Launch Service.

                OSStatus res = LSOpenCFURLRef(url, NULL);

                CFRelease(url);

                BOOLEAN_TO_NPVARIANT(res == noErr, *result);

            }

        }

        return true;

    }

    browser->memfree(name);

    return false;

}



bool hasProperty(NPObject *obj, NPIdentifier propertyName) {

    return false;

}



bool getProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result) {

    return false;

}







//NPP Functions Implements

NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved)

{

    // Create per-instance storage

    //obj = (PluginObject *)malloc(sizeof(PluginObject));

    //bzero(obj, sizeof(PluginObject));

    

    //obj->npp = instance;

    //instance->pdata = obj;

    

    if(!instance->pdata) {

        instance->pdata = browser->createobject(instance, &scriptablePluginClass);

    }

    // Ask the browser if it supports the CoreGraphics drawing model

    NPBool supportsCoreGraphics;

    if (browser->getvalue(instance, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) != NPERR_NO_ERROR)

        supportsCoreGraphics = FALSE;

    

    if (!supportsCoreGraphics)

        return NPERR_INCOMPATIBLE_VERSION_ERROR;

    

    // If the browser supports the CoreGraphics drawing model, enable it.

    browser->setvalue(instance, NPPVpluginDrawingModel, (void *)NPDrawingModelCoreGraphics);

    

    return NPERR_NO_ERROR;

}



NPError NPP_Destroy(NPP instance, NPSavedData** save)

{

    

    // If we created a plugin instance, we'll destroy and clean it up.

    NPObject *pluginInstance=instance->pdata;

    if(!pluginInstance) {

        browser->releaseobject(pluginInstance);

        pluginInstance = NULL;

    }

    

    return NPERR_NO_ERROR;

}



NPError NPP_SetWindow(NPP instance, NPWindow* window)

{

    return NPERR_NO_ERROR;

}

 



NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)

{

    *stype = NP_ASFILEONLY;

    return NPERR_NO_ERROR;

}



NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)

{

    return NPERR_NO_ERROR;

}



int32 NPP_WriteReady(NPP instance, NPStream* stream)

{

    return 0;

}



int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)

{

    return 0;

}



void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)

{

}



void NPP_Print(NPP instance, NPPrint* platformPrint)

{



}





int16_t NPP_HandleEvent(NPP instance, void* event)

{

    return 0;

}



void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)

{



}



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

{

    NPObject *pluginInstance=NULL;

    switch(variable) {

        case NPPVpluginScriptableNPObject:

            // If we didn't create any plugin instance, we create it.

            pluginInstance=instance->pdata;

            if (pluginInstance) {

                browser->retainobject(pluginInstance);

            }

            *(NPObject **)value = pluginInstance;

            break;

        default:

            return NPERR_GENERIC_ERROR;

    }

    

    return NPERR_NO_ERROR;

}



NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)

{

    return NPERR_GENERIC_ERROR;

}

测试代码

<html>

<head>

<script>

function run() {

    var plugin = document.getElementById("pluginId");

    plugin.open("http://www.geeklu.com");

}

</script>

</head>

<body >

<embed width="0" height="0" type="test/x-open-with-default-plugin" id="pluginId">

<button onclick="run()">run</button>

</body>

</html>

你可能感兴趣的:(api)