【专业技术】谷歌浏览器实现Javascript扩展
编者按:Javascript扩展有很多种,C++和IDL,纯JS等等,看看下文介绍一位前辈的博客,相信对大家有帮助。
写chromium扩展、写webui,还有一点便是如何增加extension API,下边所描述涉及两种添加extension api的方法,第一种较为麻烦,但有利于完整理解。全文参考chromium官方文档。
一.通过json文件添加扩展API
1、增加导出接口权限限制:chrome/common/extensions/api/_permission_features.json。api的名称为“myapi”,如果加上"location": "component"表示只有内部程序可以使用该api,这样子外界插件使用就会出现提示:“ 'myapi' is not allowed for specified install location.”
"myapi": {
"channel": "stable",
"extension_types": ["extension", "packaged_app","platform_app","hosted_app"]
},
2、创建myapi.json文件,注意到文件名要以unix的方式命名,文件里的namespace值要相对应。文件名为“srcchromecommonextensionsapimyapi.json”,内容如下:
[{
"namespace": "myapi",
"functions": [
{
"name": "helloWorld",
"type": "function",
"description": "Hello world by cswuyg",
"parameters": [{
"name": "data",
"type": "string",
"description": "data is hello world by cswuyg"
},
{
"name": "callback",
"type": "function",
"parameters": [{
"name": "result",
"type": "string",
"description": "result of hello world by cswuyg."
}]
}]
}]
}]
3、添加myapi.json到chrome/common/extensions/api/api.gyp,因为我用sln编译的,所以还需要自己手动把myapi.json添加到api.sln。代码片段:
'variables': {
'management.json',
'myapi.json',
......
api.sln编译通过之后,产生的C++接口文件在srcbuildDebugobjglobal_intermediatechromecommonextensionsapi下。
4、添加myapi.json文件扩展资源定义chrome/common/extensions_api_resources.grd文件。定一个ID值IDR_EXTENSION_API_JSON_MYAPI。代码片段
......
<include name="IDR_EXTENSION_API_JSON_METRICSPRIVATE" file="extensionsapimetrics_private.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_MYAPI" file="extensionsapimyapi.json" type="BINDATA" />
......
5、在chrome/common/extensions/api/extension_api.cc的ExtensionAPI::InitDefaultConfiguration函数中通过IDR_EXTENSION_API_JSON_MYAPI ID值加载myapi.json文件。
void ExtensionAPI::InitDefaultConfiguration() {
......
RegisterSchema("myapi", ReadFromResource(
IDR_EXTENSION_API_JSON_MYAPI));
......
}
6、在srcchromebrowserextensionsapimyapi文件夹下完成扩展的C++响应。
//myapi_api.h
#pragma once
#include "chrome/browser/extensions/extension_function.h"
namespace extensions {
class HelloWorldFunction : public AsyncExtensionFunction{
public:
HelloWorldFunction();
//ExtensionFunction:
virtual bool RunImpl() OVERRIDE;
protected:
virtual ~HelloWorldFunction(){};
private:
DECLARE_EXTENSION_FUNCTION_NAME("myapi.helloWorld")
};
}
//myapi_api.cpp
#include "myapi_api.h"
#include "chrome/common/extensions/api/myapi.h"
namespace extensions{
namespace myapi = api::myapi;
HelloWorldFunction::HelloWorldFunction() {}
bool HelloWorldFunction::RunImpl()
{
scoped_ptr<::extensions::myapi::HelloWorld::Params> params(
::extensions::myapi::HelloWorld::Params::Create(*args_));
results_ = myapi::HelloWorld::Results::Create(params->data + "helloWorldResult!!");
SendResponse(true);
return true;
}
}
把增加的文件,添加到src/chrome/chrome_browser_extensions.gypi文件下,下次用runhooks的时候sln里内容就不会丢失修改。
7、在chrome/browser/extensions/extension_function_registry.cc下的ExtensionFunctionRegistry::ResetFunctions()函数中注册扩展函数的响应。
void ExtensionFunctionRegistry::ResetFunctions() {
#if defined(ENABLE_EXTENSIONS)
......
RegisterFunction<extensions::HelloWorldFunction>();
......
#endif // defined(ENABLE_EXTENSIONS)
}
8、在srcchromecommonextensionspermissionsapi_permission.cc的RegisterAllPermissions函数中添加新扩展api类型的permission。这里的字符串跟扩展的manifest.json里的permissions键的值、*.json接口脚本namespace值相对应。
{ APIPermission::Kmyapi, "myapi", kFlagNone,
IDS_EXTENSION_PROMPT_WARNING_MYAPI,
PermissionMessage::kmyapi },
到这里就可以实现一个导出接口了,步骤略微繁琐,这是不推荐的遗留方式,下边介绍一种简洁的、推荐的方式。
二、通过IDL文件添加扩展API
1、增加导出接口权限限制:chrome/common/extensions/api/_permission_features.json。api的名称为“myIdl”。
"myIdl": {
"channel": "stable",
"extension_types": ["extension", "packaged_app","platform_app","hosted_app"]
},
2、创建my_idl.idl文件,把它加入到api.gyp 中,使用runhooks重新生成解决方案,或者修改api.sln工程里的api_rules.mk文件的属性。
my_idl.idl文件:
[permissions=myIdl]namespace myIdl {
callback HelloWorld2Callback = void (DOMString result);
interface Functions {
static void HelloWorld2(DOMString input, HelloWorld2Callback callback);
};
};
api.gyp:
'media_galleries.idl',
'media_galleries_private.idl',
'my_idl.idl',
编译之后产生my_idl.h、my_idl.cpp文件
3、 在srcchromecommonextensionspermissionsapi_permission.cc的RegisterAllPermissions函数中添加新扩展api类型的permission。这里的字符串跟扩展的manifest.json里的permissions键的值相对应,跟idl文件里的namespace值对应。
{ APIPermission::KmyIdl, "myIdl", kFlagNone,
IDS_EXTENSION_PROMPT_WARNING_MYAPI,
PermissionMessage::kmyIdl },
4、添加实现文件在srcchromebrowserextensionsapi下,需要注意DECLARE_EXTENSION_FUNCTION_NAME定义的函数名要跟idl里定义的接口名一样,否则会在output中提示不存在该函数。而类名,则要跟自动生成的srcbuildDebugobjglobal_intermediatechromecommonextensionsapigenerated_api.h里注册的名称一致。
my_idl.h文件:
#pragma once
#include "chrome/browser/extensions/extension_function.h"
namespace extensions {
class MyIdlHelloWorld2Function : public AsyncExtensionFunction{
public:
MyIdlHelloWorld2Function();
//ExtensionFunction:
virtual bool RunImpl() OVERRIDE;
protected:
virtual ~MyIdlHelloWorld2Function(){};
private:
DECLARE_EXTENSION_FUNCTION_NAME("myIdl.HelloWorld2")
};
}
my_idl.cc文件:
#include "my_idl_api.h"
#include "chrome/common/extensions/api/my_idl.h"
namespace extensions{
namespace myIdl = api::my_idl;
MyIdlHelloWorld2Function::MyIdlHelloWorld2Function() {}
bool MyIdlHelloWorld2Function::RunImpl()
{
scoped_ptr<::extensions::myIdl::HelloWorld2::Params> params(
::extensions::myIdl::HelloWorld2::Params::Create(*args_));
results_ = myIdl::HelloWorld2::Results::Create(params->input + " result by cswuyg");
SendResponse(true);
return true;
}
}
到这里就可以了,步骤明显比legacy方式简单多了。
三、实现一个内置扩展
如果需要做一个内部的扩展功能,但是又不希望这个扩展是可选的,也就是说这是浏览器自带的一个默认扩展, 就像chrome://bookmarks,该怎么做呢?
1、创建扩展的web资源文件,在srcchromebrowserresourcesmyapi文件夹下,其中manifest.json文件的“key”的来源:通过载入一个自定义的crx插件然后在chromium的appdata下找到该插件的key,该key可能被用于计算extension-id,如果扩展无法调用接口,有可能是key不对,在srcbasebase64.cc的Base64Decode函数处做检查。
manifest.json:
{
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDi4h7GFx0NvfD3EhvKNf7bAp2/U+0Be92WDRgkkOG8l+73weIDb7/UpZ831JSFxnjtOoKj7PLTYk//tYd3ZYhIdnZfPap6M6s0v8nzibCkvqCbsChg7EbuJ6Cf3l4upU+0QTPHYKswcDBkMg6oNrRj3vhWeKUEBPktBu99/S2MKwIDAQAB",
"name": "myapi_cswuyg",
"version": "1.0",
"manifest_version": 2,
"description": "myapi",
"permissions": ["myapi"],
"chrome_url_overrides": {
"cswuyg": "popup.html"
},
"content_security_policy": "object-src 'self'; script-src chrome://resources 'self'"
}
popup.html:
<html>
<body>
<p>Hello World</p>
<script type="text/javascript" src="test.js">
</script>
</body>
</html>
test.js:
function myapi() {
var input = {};
input.data = 'helloworld';
input.name = 'cswuyg';
var strInput = JSON.stringify(input);
chrome.myapi.helloWorld((strInput), function(data){
alert('result = ' + data);
});
}
chrome.browserAction.onClicked.addListener(myapi);
myapi();
2、把web资源添加到srcchromebrowserresourcescomponent_extension_resources.grd文件中,用于资源打包,必须把除了mainfest.json文件之外的其他独立文件都加进来。
<include name="IDR_MYAPI_MAIN" file="myapi/popup.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_MYAPI_COMMON_JS" file="myapi/test.js" type="BINDATA" />
3、添加ID跟扩展资源的对应关系。在:srcchromebrowserbrowser_resources.grd 文件下,定义一个ID IDR_MYAPI_MANIFEST跟扩展mainfest.json的关联。
<include name="IDR_MYAPI_MANIFEST" file="resourcesmyapimanifest.json" type="BINDATA" />
4、添加IDR_MYAPI_MANIFEST对应的扩展的加载放到 srcchromebrowserextensionscomponent_loader.cc文件下,AddDefaultComponentExtensions()函数中
Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print")));
Add(IDR_MYAPI_MANIFEST, FilePath(FILE_PATH_LITERAL("myapi")));
5、在srcchromecommonextensionsextension.cc文件的LoadChromeURLOverrides函数下增加被扩展函数的override逻辑。
// Restrict override pages to a list of supported URLs.
bool is_override = (page != chrome::kChromeUINewTabHost &&
page != chrome::kChromeUIBookmarksHost &&
page != chrome::kChromeUIHistoryHost &&
page != "cswuyg"
);
这样子就可以使用chrome:\cswuyg打开页面了,该页面使用的是扩展的API,同时它打包在resource.pak里。
特别注意,manifest.json中的chrome_url_overrides的键必须跟LoadChromeURLOverrides里添加的Host名称一致。如果要修改chrome://...的hostname名称需要修改两个地方,一个是manifest.json的chrome_url_overrides的子键,一个是extension.cc文件LoadChromeURLOverride函数里“Restrict override pages to a list of supported URLs.”处的Host名称。
使用*.json添加扩展API,可以参考top_site.json的实现。
使用*.idl添加扩展API,可以参考downloads.idl的实现。
添加内置扩展,可以参考chrome://bookmarks的实现。
转自:http://www.cnblogs.com/cswuyg/archive/2013/03/13/2956863.html
- 使用Spring Cloud搭建服务注册中心
- 技术分享 | kafka的使用场景以及生态系统
- WebSocket刨根问底(二)
- WebSocket刨根问底(三)之群聊
- SDNLAB群分享(四):利用ODL下发流表创建VxLAN网络
- 一个简单的案例带你入门Dubbo分布式框架
- Ajax上传图片以及上传之前先预览
- Spring Cloud中Hystrix的服务降级与异常处理
- Open vSwitch源码解析之基于VxLAN实现NSH解析功能
- Spring Cloud自定义Hystrix请求命令
- JavaScript面试问题:事件委托和this
- Spring Cloud中的断路器Hystrix
- js的隐含参数(arguments,callee,caller)使用方法
- Spring Cloud中的负载均衡策略
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- GitHub-分支管理03-多人合作【重点】
- 宝塔面板phpMyAdmin未授权访问漏洞是个低级错误吗?
- GitHub-标签管理
- 经典写配置漏洞与几种变形
- GitLab安装及使用
- 持续集成-Jenkins安装部署
- 谈一谈Linux与suid提权
- PHP动态特性的捕捉与逃逸
- ES6 随性学习之 新增数据类型 Symbol
- 最简单入门深度学习
- 持续集成-Jenkins常用插件安装
- 小白学PyTorch | 15 TF2实现一个简单的服装分类任务
- 小白学PyTorch | 16 TF2读取图片的方法
- 小白学PyTorch | 17 TFrec文件的创建与读取
- 小白学PyTorch | 18 TF2构建自定义模型