chromium如何以json方式新增extension API以及添加內部擴展

轉自:http://www.cnblogs.com/cswuyg/archive/2013/03/13/2956863.html

寫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值要相對應。文件名爲“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。代碼片段
         ......
        <include name="IDR_EXTENSION_API_JSON_METRICSPRIVATE" file="extensions\api\metrics_private.json" type="BINDATA" />
        <include name="IDR_EXTENSION_API_JSON_MYAPI" file="extensions\api\myapi.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));
      ......
}

目前已經變爲了chrome/common/extensions/api/chrome_extensions_client.ccChromeExtensionsClient::RegisterAPISchemaResources函數中通過IDR_EXTENSION_API_JSON_MYAPI ID值加載myapi.json文件。 

void ChromeExtensionsClient::RegisterAPISchemaResources(
    ExtensionAPI* api) const {
#if defined(ENABLE_EXTENSIONS)
  api->RegisterSchemaResource("accessibilityPrivate",

		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、在extensions/browser/extension_function_registry.cc下的ExtensionFunctionRegistry::ResetFunctions()函數中註冊擴展函數的響應。
複製代碼
void ExtensionFunctionRegistry::ResetFunctions() {
#if defined(ENABLE_EXTENSIONS)
......
  RegisterFunction<extensions::HelloWorldFunction>();
......
#endif  // defined(ENABLE_EXTENSIONS)
}
複製代碼
目前這一塊與已經由腳本tools\json_schema_compiler\cpp_bundle_generator.py自動處理,不需要單獨添加了。

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 }, 
 這塊目前放在extensions\common\permissions\extensions_api_permissions.cc或者chrome\common\extensions\permissions\chrome_api_permissions.cc文件的GetAllPermissions()函數來實現。
到這裏就可以實現一個導出接口了,步驟略微繁瑣,這是不推薦的遺留方式,下邊介紹一種簡潔的、推薦的方式。
 
二、通過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:

複製代碼
<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資源添加到src\chrome\browser\resources\component_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跟擴展資源的對應關係。在:src\chrome\browser\browser_resources.grd 文件下,定義一個ID IDR_MYAPI_MANIFEST跟擴展mainfest.json的關聯。
      <include name="IDR_MYAPI_MANIFEST" file="resources\myapi\manifest.json" type="BINDATA" />
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的實現。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章