开发附带NPAPI插件的Chrome扩展.docx
《开发附带NPAPI插件的Chrome扩展.docx》由会员分享,可在线阅读,更多相关《开发附带NPAPI插件的Chrome扩展.docx(12页珍藏版)》请在冰豆网上搜索。
![开发附带NPAPI插件的Chrome扩展.docx](https://file1.bdocx.com/fileroot1/2022-11/25/a267bbae-bd8f-4331-9018-1e1658dd15a1/a267bbae-bd8f-4331-9018-1e1658dd15a11.gif)
开发附带NPAPI插件的Chrome扩展
开发附带NPAPI插件的Chrome扩展
1NPAPI插件2
1.1NPAPI简介2
1.2准备工作2
1.3创建插件2
1.3.1创建Win32DLL工程2
1.3.2引入NPAPI库2
1.3.3添加宏定义_X86_2
1.3.4添加模块定义文件(.def文件)2
1.3.5编辑stdafx.h文件3
1.3.6添加基础框架文件3
1.3.7编辑sample.cpp文件3
1.3.8编辑sample.h文件4
1.3.9添加Version资源8
1.3.10编译输出8
1.4需要注意的问题8
1.4.1库文件的捆绑8
1.4.2谨记MIMEType8
1.4.3无效的NPN_CreateObject?
8
2Chrome扩展9
2.1简介9
2.2开始编写9
2.2.1准备一个图标文件(.png)9
2.2.2准备NPAPI插件(.dll)9
2.2.3编写manifest.json9
2.2.4编写background.html10
2.2.5编写background.js10
2.3安装与测试10
2.4发布10
3参考资料11
1NPAPI插件
1.1NPAPI简介
NPAPI(NetscapePluginApplicationProgrammingInterface,网景插件应用程序接口)是网景公司当年制定的开发基于网景浏览器,用于在浏览器中执行外部应用程序的通用接口。
该接口基于插件机制,制定了一系列的标准和API,因此也有NPAPI插件一说。
同期的微软,也在IE中支持ActiveX为浏览器插件,不得不承认微软在这一点上,把浏览器和OS结合的更为紧密,这也可能是当年微软能够击溃网景的原因之一。
但网景的影响深远,除了微软特立独行之外,其他浏览器开发厂商奇迹般的都一致采用了NPAPI来对浏览器进行扩展(这包括后来从灰烬中重生的FireFox及新生的Chrome;当然,Chrome在不久前时间已经在尝试抛弃NPAPI了)。
因此,在目前来看NPAPI几乎是IE之外的浏览器插件开发的统一标准。
1.2准备工作
根据参考资料,从ftp:
//ftp.mozilla.org/pub/mozilla.org/firefox/releases/4.0.1/source/中下载了firefox早期的源代码,并从中抠出了NPAPI相关的部分(恩,资料上说的不够详细)。
解压源代码,把modules\plugin\base\public和modules\plugin\sdk\samples\include两个目录中的文件复制出来放在一起(我创建了D:
\npapi,把文件都放这了)。
另外,资料里提到的三个文件在modules\plugin\sdk\samples\common下(记住位置,待会会用到)。
1.3创建插件
顺便提一下,本文以VS2003为范例。
插件实现的功能:
对浏览器(贴切点说是对javascript引擎)暴露对象Sample,而Sample又提供了一个sayHello的方法。
这样一来,我们可以在浏览器中,使用javascript通过Sample.sayHello();来调用插件所提供的功能。
1.3.1创建Win32DLL工程
1.3.2引入NPAPI库
在工程属性中,添加“附加包含目录”:
D:
\npapi(之前抠出来的部分)。
1.3.3添加宏定义_X86_
1.3.4添加模块定义文件(.def文件)
创建sample.def文件,内容为:
LIBRARY"sample"
EXPORTS
NP_GetEntryPoints@1
NP_Initialize@2
NP_Shutdown@3
1.3.5编辑stdafx.h文件
增加tchar头文件的引入:
#include
增加NPAPI头文件的引入:
//Mozilla-API
#include
#include
#include"npruntime.h"
1.3.6添加基础框架文件
找到np_entry.cpp、npn_gate.cpp和npp_gate.cpp,复制到工程目录下,并添加到工程(恩恩,位置在modules\plugin\sdk\samples\common)。
在编辑器里分别打开着三个文件,并在文件头部加入:
#include"stdafx.h"
1.3.7编辑sample.cpp文件
将文件代码修改为:
#include"stdafx.h"
#include"sample.h"
NPErrorNS_PluginInitialize()
{
returnNPERR_NO_ERROR;
}
voidNS_PluginShutdown()
{
}
nsPluginInstanceBase*NS_NewPluginInstance(nsPluginCreateData*aCreateDataStruct)
{
if(!
aCreateDataStruct)
returnNULL;
CPlugin*plugin=newCPlugin(aCreateDataStruct->instance);
returnplugin;
}
voidNS_DestroyPluginInstance(nsPluginInstanceBase*aPlugin)
{
if(aPlugin)
delete(CPlugin*)aPlugin;
}
1.3.8编辑sample.h文件
如果文件不存在,创建一个,并添加到工程。
将文件内容修改为:
#include"stdafx.h"
#include"npruntime.h"
#include"pluginbase.h"
boolIsStringNPIdentifier(NPIdentifiername)
{
return*(char**)name==(char*)name+8;
}
char*CopyNPString(NPStringstr)
{
char*r=newchar[str.UTF8Length+1];
strncpy(r,str.UTF8Characters,str.UTF8Length);
r[str.UTF8Length]=0;
returnr;
}
classCSample:
publicNPObject
{
public:
CSample(NPPnpp):
mNpp(npp){}
~CSample(){}
staticNPObject*_Creator(NPPnpp,NPClass*aClass){returnnewCSample(npp);}
staticvoid_Deallocate(NPObject*npobj){delete(CSample*)npobj;}
staticvoid_Invalidate(NPObject*npobj){((CSample*)npobj)->Invalidate();}
staticbool_HasMethod(NPObject*npobj,NPIdentifiername){return((CSample*)npobj)->HasMethod(name);}
staticbool_Invoke(NPObject*npobj,NPIdentifiername,constNPVariant*args,uint32_targCount,NPVariant*result){return((CSample*)npobj)->Invoke(name,args,argCount,result);}
staticbool_InvokeDefault(NPObject*npobj,constNPVariant*args,uint32_targCount,NPVariant*result){return((CSample*)npobj)->InvokeDefault(args,argCount,result);}
staticbool_HasProperty(NPObject*npobj,NPIdentifiername){return((CSample*)npobj)->HasProperty(name);}
staticbool_GetProperty(NPObject*npobj,NPIdentifiername,NPVariant*result){return((CSample*)npobj)->GetProperty(name,result);}
staticbool_SetProperty(NPObject*npobj,NPIdentifiername,constNPVariant*value){return((CSample*)npobj)->SetProperty(name,value);}
staticbool_RemoveProperty(NPObject*npobj,NPIdentifiername){return((CSample*)npobj)->RemoveProperty(name);}
staticbool_Enumerate(NPObject*npobj,NPIdentifier**identifier,uint32_t*count){return((CSample*)npobj)->Enumerate(identifier,count);}
staticbool_Construct(NPObject*npobj,constNPVariant*args,uint32_targCount,NPVariant*result){return((CSample*)npobj)->Construct(args,argCount,result);}
virtualvoidInvalidate(){}
virtualboolHasMethod(NPIdentifiername)
{
if(IsStringNPIdentifier(name))
{
char*methodName=*(char**)name;
if(_tcscmp(methodName,TEXT("sayHello"))==0)
returntrue;
}
returnfalse;
}
virtualboolInvoke(NPIdentifiername,constNPVariant*args,uint32_targCount,NPVariant*result)
{
if(IsStringNPIdentifier(name))
{
char*methodName=*(char**)name;
if(_tcscmp(methodName,TEXT("sayHello"))==0)
{
MessageBox(NULL,TEXT("hello,npapi."),TEXT("plugin-sample"),MB_OK|MB_ICONINFORMATION);
returntrue;
}
}
returnfalse;
}
virtualboolInvokeDefault(constNPVariant*args,uint32_targCount,NPVariant*result){returntrue;}
virtualboolHasProperty(NPIdentifiername){returnfalse;}
virtualboolGetProperty(NPIdentifiername,NPVariant*result){returnfalse;}
virtualboolSetProperty(NPIdentifiername,constNPVariant*value){returnfalse;}
virtualboolRemoveProperty(NPIdentifiername){returnfalse;}
virtualboolEnumerate(NPIdentifier**identifier,uint32_t*count){returnfalse;}
virtualboolConstruct(constNPVariant*args,uint32_targCount,NPVariant*result){returnfalse;}
private:
NPPmNpp;
};
staticNPClassSample={
NP_CLASS_STRUCT_VERSION_CTOR,
CSample:
:
_Creator,
CSample:
:
_Deallocate,
CSample:
:
_Invalidate,
CSample:
:
_HasMethod,
CSample:
:
_Invoke,
CSample:
:
_InvokeDefault,
CSample:
:
_HasProperty,
CSample:
:
_GetProperty,
CSample:
:
_SetProperty,
CSample:
:
_RemoveProperty,
CSample:
:
_Enumerate,
CSample:
:
_Construct
};
classCPlugin:
publicnsPluginInstanceBase
{
public:
CPlugin(NPPpNPInstance):
nsPluginInstanceBase(),m_pNPInstance(pNPInstance),m_bInitialized(FALSE),m_sample(NULL){}
~CPlugin(){}
NPBoolinit(NPWindow*pNPWindow)
{
m_bInitialized=TRUE;
returnTRUE;
}
voidshut()
{
if(m_sample)
{
//NPN_ReleaseObject(m_sample);
deletem_sample;
m_sample=NULL;
}
m_bInitialized=FALSE;
}
NPBoolisInitialized()
{
returnm_bInitialized;
}
NPErrorGetValue(NPPVariablevariable,void*value)
{
switch(variable)
{
caseNPPVpluginNameString:
*((char**)value)="plugin-sample";
break;
caseNPPVpluginDescriptionString:
*((char**)value)="plugin-sampleforChrome";
break;
caseNPPVpluginScriptableNPObject:
//if(m_sample==NULL)
//m_sample=(CSample*)NPN_CreateObject(m_pNPInstance,&Sample);
//
//if(m_sample!
=NULL)
//NPN_RetainObject(m_sample);
if(m_sample==NULL)
{
m_sample=newCSample(m_pNPInstance);
m_sample->_class=&Sample;
}
*((NPObject**)value)=m_sample;
break;
}
returnnsPluginInstanceBase:
:
GetValue(variable,value);
}
private:
NPPm_pNPInstance;
NPBoolm_bInitialized;
CSample*m_sample;
};
1.3.9添加Version资源
以文本编辑器方式打开资源文件,在版本信息BLOCK中添加:
VALUE"MIMEType","application/plugin-sample"
1.3.10编译输出
自此,sample.dll已经躺在Debug目录下了。
1.4需要注意的问题
1.4.1库文件的捆绑
考虑到工程的独立性,我们可以把库文件与工程捆绑在一起,我的做法是在工程内创建一个inc目录,把之前提到的D:
\npapi下所有文件复制过来,并把“附加包含目录”改为:
inc。
1.4.2谨记MIMEType
一定要记得添加Version资源,并添加MIMEType项。
1.4.3无效的NPN_CreateObject?
在后续的测试过程中,NPN_CreateObject总是无法有效的创建对象。
因此,在sample.h中,我们采用了直接newCSample();的方式(具体原因有待研究)。
2Chrome扩展
2.1简介
不愧是Google出品,Chrome从一推出就受到了业界大量的关注和用户的青睐,几年下来,市场份额一直在膨胀。
其中原因不仅是小巧轻量和启动快速,也有其快速支持最新Web标准等多方面的缘故。
对于第三方开发商,Google也提供了Chrome扩展编程接口,用来提升浏览器本身的个性化定制。
Chrome扩展基于HTML5构建,面向javascript引擎暴露浏览器内部对象,使用javascript即可直接操作浏览器对象,从而实现功能扩展。
当然,如果我们希望实现的功能超出了Chrome本身提供的内置对象所涵盖的范围,则需要之前提到的NPAPI插件的支持了(这就类似IE浏览器中通过newActiveXObject创建COM对象来增强浏览器功能一样)。
2.2开始编写
哦哦,提醒一下,下面提到的所有文件,务必放到同一个目录中。
扩展实现的功能:
在每个页面(空白处)的右键菜单中,添加“sayHello”菜单项,用户点击这个菜单项时,扩展程序通过调用NPAPI插件的sayHello方法,实现弹出“hello,npapi.”对话框的功能。
2.2.1准备一个图标文件(.png)
去网上下载一个png文件吧,32x32、48x48、64x64、128x128等尺寸的都可以。
总之,这是一件彰显个性的事情。
2.2.2准备NPAPI插件(.dll)
恩,之前编译好,已经在Debug目录躺的妥妥的sample.dll,把他复制过来吧。
2.2.3编写manifest.json
内容如下:
{
"manifest_version":
2,
"minimum_chrome_version":
"6.0.0.0",
"name":
"我的扩展",
"description":
"我的扩展",
"version":
"1.0.0",
"permissions":
[
"contextMenus",
"tabs",
"http:
//*/*",
"https:
//*/*"
],
"icons":
{
"128":
"sayHello.png"
},
"background":
{
"page":
"background.html"
},
"plugins":
[
{"path":
"sample.dll","public":
true}
]
}
2.2.4编写background.html
内容如下:
2.2.5编写background.js
内容如下:
varbkgnd=chrome.extension.getBackgroundPage();
varsample=bkgnd.document.getElementById("Sample");
functiongetClickHandler(type){
returnfunction(info,tab){
varurl=info.pageUrl;
vartitle=tab.title;
if(type=="page"){
sample.sayHello();
}
}
}
chrome.contextMenus.create({"title":
"sayHello","type":
"normal","contexts":
["page"],"onclick":
getClickHandler("page")});
2.3安装与测试
打开Chrome设置的“扩展程序”页面,勾选“开发者模式”,点击“加载正在开发的扩展程序”,在弹出的对话框中,选择Chrome扩展所在的目录,然后再确认“添加”即可。
2.4发布
同上,在开发者模式下,选择“打包扩展程序”,在弹出的对话框中,选择Chrome扩展所在的目录,然后再次点击“打包扩展程序”即可(第一次打包时,Chrome会自动生成一个密钥文件;以后每次打包,都需要选择这个密钥文件)。
打包之后的Chrome扩展,是一个.crx的zip压缩文件,可以直接拖拽到Chrome的扩展程序页面,实现安装。
3参考资料
Mozilla官方文档(英文):
https:
//developer.mozilla.org/en-US/docs/Plugins
https:
//developer.mozilla.org/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Basics
NPAPI开发详解(中文):
http:
//