【專業技術】谷歌瀏覽器實現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