chromium如何新增extension API以及添加内部扩展

343 阅读5分钟

利于完整理解。全文参考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值要相对应。文件名为“src\chrome\common\extensions\api\myapi.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++接口文件在src\build\Debug\obj\global_intermediate\chrome\common\extensions\api下。 

4、添加myapi.json文件扩展资源定义chrome/common/extensions_api_resources.grd文件。定一个ID值IDR_EXTENSION_API_JSON_MYAPI。代码片段 

        ...... 
 
     
......  

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、在src\chrome\browser\extensions\api\myapi文件夹下完成扩展的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) 
...... 
RegisterFunctionextensions::HelloWorldFunction(); 
...... 
#endif  // defined(ENABLE_EXTENSIONS) 
}  

8、在src\chrome\common\extensions\permissions\api_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、 在src\chrome\common\extensions\permissions\api_permission.cc的RegisterAllPermissions函数中添加新扩展api类型的permission。这里的字符串跟扩展的manifest.json里的permissions键的值相对应,跟idl文件里的namespace值对应。 

{ APIPermission::KmyIdl, "myIdl", kFlagNone, 
IDS_EXTENSION_PROMPT_WARNING_MYAPI, 
PermissionMessage::kmyIdl },  

4、添加实现文件在\src\chrome\browser\extensions\api\下,需要注意DECLARE_EXTENSION_FUNCTION_NAME定义的函数名要跟idl里定义的接口名一样,否则会在output中提示不存在该函数。而类名,则要跟自动生成的src\build\Debug\obj\global_intermediate\chrome\common\extensions\api\generated_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资源文件,在src\chrome\browser\resources\myapi文件夹下,其中manifest.json文件的“key”的来源:通过载入一个自定义的crx插件然后在chromium的appdata下找到该插件的key,该key可能被用于计算extension-id,如果扩展无法调用接口,有可能是key不对,在src\base\base64.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: 

       

Hello World

            

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资源添加到src\chrome\browser\resources\component_extension_resources.grd文件中,用于资源打包,必须把除了mainfest.json文件之外的其他独立文件都加进来。 

  
  

3、添加ID跟扩展资源的对应关系。在:src\chrome\browser\browser_resources.grd 文件下,定义一个ID IDR_MYAPI_MANIFEST跟扩展mainfest.json的关联。 

       

4、添加IDR_MYAPI_MANIFEST对应的扩展的加载放到 src\chrome\browser\extensions\component_loader.cc文件下,AddDefaultComponentExtensions()函数中 

 Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print"))); 
Add(IDR_MYAPI_MANIFEST, FilePath(FILE_PATH_LITERAL("myapi")));  

5、在src\chrome\common\extensions\extension.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的实现。