您的位置:首页 > 其它

开发顺带NPAPI插件的Chrome扩展

2015-12-30 18:47 344 查看
开发附带NPAPI插件的Chrome扩展



1 NPAPI插件

1.1 NPAPI简介

NPAPI(Netscape Plugin Application Programming Interface,网景插件应用程序接口)是网景公司当年制定的开发基于网景浏览器,用于在浏览器中执行外部应用程序的通用接口。该接口基于插件机制,制定了一系列的标准和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 创建Win32 DLL工程

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<tchar.h>

增加NPAPI头文件的引入:

//Mozilla-API

#include<npfunctions.h>

#include<npruntime.h>

#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()

{

return NPERR_NO_ERROR;

}

voidNS_PluginShutdown()

{

}

nsPluginInstanceBase* NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)

{

if(!aCreateDataStruct)

return NULL;

CPlugin* plugin = newCPlugin(aCreateDataStruct->instance);

return plugin;

}

voidNS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)

{

if(aPlugin)

delete (CPlugin *)aPlugin;

}

1.3.8 编辑sample.h文件

如果文件不存在,创建一个,并添加到工程。将文件内容修改为:

#include"stdafx.h"

#include"npruntime.h"

#include"pluginbase.h"

boolIsStringNPIdentifier(NPIdentifier name)

{

return *(char**)name == (char*)name + 8;

}

char*CopyNPString(NPString str)

{

char* r= new char[str.UTF8Length + 1];

strncpy(r, str.UTF8Characters,str.UTF8Length);

r[str.UTF8Length] = 0;

return r;

}

classCSample : public NPObject

{

public:

CSample(NPP npp) : mNpp(npp) { }

~CSample() { }

static NPObject* _Creator(NPP npp,NPClass *aClass) { return new CSample(npp); }

static void _Deallocate(NPObject *npobj){ delete (CSample*)npobj; }

static void _Invalidate(NPObject *npobj){ ((CSample*)npobj)->Invalidate(); }

static bool _HasMethod(NPObject *npobj,NPIdentifier name) { return ((CSample*)npobj)->HasMethod(name); }

static bool _Invoke(NPObject *npobj,NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result){ return ((CSample*)npobj)->Invoke(name, args, argCount, result); }

static bool _InvokeDefault(NPObject*npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return((CSample*)npobj)->InvokeDefault(args, argCount, result); }

static bool _HasProperty(NPObject *npobj, NPIdentifier name) { return ((CSample*)npobj)->HasProperty(name); }

static bool _GetProperty(NPObject *npobj,NPIdentifier name, NPVariant *result) { return((CSample*)npobj)->GetProperty(name, result); }

static bool _SetProperty(NPObject *npobj,NPIdentifier name, const NPVariant *value) { return((CSample*)npobj)->SetProperty(name, value); }

static bool _RemoveProperty(NPObject*npobj, NPIdentifier name) { return ((CSample*)npobj)->RemoveProperty(name);}

static bool _Enumerate(NPObject *npobj,NPIdentifier **identifier, uint32_t *count) { return((CSample*)npobj)->Enumerate(identifier, count); }

static bool _Construct(NPObject *npobj,const NPVariant *args, uint32_t argCount, NPVariant *result) { return((CSample*)npobj)->Construct(args, argCount, result); }

virtual void Invalidate() { }

virtual bool HasMethod(NPIdentifier name)

{

if(IsStringNPIdentifier(name))

{

char* methodName = *(char**)name;

if(_tcscmp(methodName,TEXT("sayHello")) == 0)

return true;

}

return false;

}

virtual bool Invoke(NPIdentifier name,const NPVariant *args, uint32_t argCount, 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);

return true;

}

}

return false;

}

virtual bool InvokeDefault(constNPVariant *args, uint32_t argCount, NPVariant *result) { return true; }

virtual bool HasProperty(NPIdentifiername) { return false; }

virtual bool GetProperty(NPIdentifiername, NPVariant *result) { return false; }

virtual bool SetProperty(NPIdentifiername, const NPVariant *value) { return false; }

virtual bool RemoveProperty(NPIdentifiername) { return false; }

virtual bool Enumerate(NPIdentifier**identifier, uint32_t *count) { return false; }

virtual bool Construct(const NPVariant*args, uint32_t argCount, NPVariant *result) { return false; }

private:

NPP mNpp;

};

staticNPClass Sample = {

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 : public nsPluginInstanceBase

{

public:

CPlugin(NPP pNPInstance) :nsPluginInstanceBase(), m_pNPInstance(pNPInstance), m_bInitialized(FALSE),m_sample(NULL) { }

~CPlugin() { }

NPBool init(NPWindow* pNPWindow)

{

m_bInitialized = TRUE;

return TRUE;

}

void shut()

{

if(m_sample)

{

// NPN_ReleaseObject(m_sample);

delete m_sample;

m_sample = NULL;

}

m_bInitialized = FALSE;

}

NPBool isInitialized()

{

return m_bInitialized;

}

NPError GetValue(NPPVariable variable,void *value)

{

switch(variable)

{

case NPPVpluginNameString:

*((char**)value) ="plugin-sample";

break;

case NPPVpluginDescriptionString:

*((char**)value) ="plugin-sample for Chrome";

break;

case NPPVpluginScriptableNPObject:

// 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:

NPP m_pNPInstance;

NPBool m_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中,我们采用了直接new CSample();的方式(具体原因有待研究)。

2 Chrome扩展

2.1 简介

不愧是Google出品,Chrome从一推出就受到了业界大量的关注和用户的青睐,几年下来,市场份额一直在膨胀。其中原因不仅是小巧轻量和启动快速,也有其快速支持最新Web标准等多方面的缘故。对于第三方开发商,Google也提供了Chrome扩展编程接口,用来提升浏览器本身的个性化定制。

Chrome扩展基于HTML5构建,面向javascript引擎暴露浏览器内部对象,使用javascript即可直接操作浏览器对象,从而实现功能扩展。当然,如果我们希望实现的功能超出了Chrome本身提供的内置对象所涵盖的范围,则需要之前提到的NPAPI插件的支持了(这就类似IE浏览器中通过new ActiveXObject创建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

内容如下:

<html>

<head></head>

<body>

<embedtype="application/plugin-sample"id="Sample"></embed>

<scripttype="text/javascript"src="background.js"></script>

</body>

</html>

2.2.5 编写background.js

内容如下:

var bkgnd= chrome.extension.getBackgroundPage();

var sample= bkgnd.document.getElementById("Sample");

functiongetClickHandler(type) {

return function(info, tab) {

var url = info.pageUrl;

var title = 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://mozilla.com.cn/post/21666/ http://wenku.baidu.com/view/c4b939f59e314332396893ce.html
Chrome扩展官方文档(英文):
https://developer.chrome.com/extensions/index.html
Chrome扩展(中文):
http://open.chrome.360.cn/extension_dev/overview.html

4 本文代码

http://download.csdn.net/detail/lonely001/6419841

来源:http://www.myexception.cn/internet/1455902.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: