`
helpbs
  • 浏览: 1172116 次
文章分类
社区版块
存档分类
最新评论

NPAPI 插件 编程起步

 
阅读更多

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

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

  1. http://colonelpanic.net/category/plugindev/npapi-plugindev/
  2. 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中找有没有这个名字的属性,然后在找方法吧。
最后终于写出了一个简单的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#import <WebKit/npapi.h>
#import <WebKit/npfunctions.h>
#import <WebKit/npruntime.h>



// Browser function table,可以通过它来得到浏览器提供的功能
staticNPNetscapeFuncs*browser;
staticconstchar*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);
voidNP_Shutdown(void);

//NPP Functions
NPError NPP_New(NPMIMEType pluginType,NPP instance,uint16_tmode,int16_targc,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);
int32NPP_WriteReady(NPP instance,NPStream*stream);
int32NPP_Write(NPP instance,NPStream*stream,int32offset,int32len,void*buffer);
voidNPP_StreamAsFile(NPP instance,NPStream*stream,constchar*fname);
voidNPP_Print(NPP instance,NPPrint*platformPrint);
int16_tNPP_HandleEvent(NPP instance,void*event);
voidNPP_URLNotify(NPP instance,constchar*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,constNPVariant*args,uint32_targCount,NPVariant*result);
bool hasProperty(NPObject*obj,NPIdentifier propertyName);
bool getProperty(NPObject*obj,NPIdentifier propertyName,NPVariant*result);
////////////////////////////////////

staticstructNPClass 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;
returnNPERR_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;

returnNPERR_NO_ERROR;
}


voidNP_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);
returnresult;
}
bool plugin_invoke(NPObject*obj,NPIdentifier methodName,constNPVariant*args,uint32_targCount,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,(constUInt8*)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);
}
}
returntrue;
}
browser->memfree(name);
returnfalse;
}

bool hasProperty(NPObject*obj,NPIdentifier propertyName){
returnfalse;
}

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



//NPP Functions Implements
NPError NPP_New(NPMIMEType pluginType,NPP instance,uint16_tmode,int16_targc,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)
returnNPERR_INCOMPATIBLE_VERSION_ERROR;

// If the browser supports the CoreGraphics drawing model, enable it.
browser->setvalue(instance,NPPVpluginDrawingModel,(void*)NPDrawingModelCoreGraphics);

returnNPERR_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;
}

returnNPERR_NO_ERROR;
}

NPError NPP_SetWindow(NPP instance,NPWindow*window)
{
returnNPERR_NO_ERROR;
}


NPError NPP_NewStream(NPP instance,NPMIMEType type,NPStream*stream,NPBool seekable,uint16_t*stype)
{
*stype=NP_ASFILEONLY;
returnNPERR_NO_ERROR;
}

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

int32NPP_WriteReady(NPP instance,NPStream*stream)
{
return0;
}

int32NPP_Write(NPP instance,NPStream*stream,int32offset,int32len,void*buffer)
{
return0;
}

voidNPP_StreamAsFile(NPP instance,NPStream*stream,constchar*fname)
{
}

voidNPP_Print(NPP instance,NPPrint*platformPrint)
{

}


int16_tNPP_HandleEvent(NPP instance,void*event)
{
return0;
}

voidNPP_URLNotify(NPP instance,constchar*url,NPReason reason,void*notifyData)
{

}

NPError NPP_GetValue(NPP instance,NPPVariable variable,void*value)
{
NPObject*pluginInstance=NULL;
switch(variable){
caseNPPVpluginScriptableNPObject:
// If we didn't create any plugin instance, we create it.
pluginInstance=instance->pdata;
if(pluginInstance){
browser->retainobject(pluginInstance);
}
*(NPObject**)value=pluginInstance;
break;
default:
returnNPERR_GENERIC_ERROR;
}

returnNPERR_NO_ERROR;
}

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

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<script>
function run() {
var plugin = document.getElementById("pluginId");
plugin.open("http://www.geeklu.com");
}
</script>
</head>
<body>
<embedwidth="0"height="0"type="test/x-open-with-default-plugin"id="pluginId">
<buttononclick="run()">run</button>
</body>
</html>

8 Responses toNPAPI 插件 编程起步

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics