创建新的Chromium extension api

不同编程语言的开发效率是不一样的,比如c++可以实现一些很底层的功能,但是开发速度比较慢,对开发者的要求也很高。而javascript开发速度很快,编程的抽象层次很高,无法调用一些系统底层的api。于是我们想到可以把c++与javascript结合起来,c++给javascript提供一些底层的api接口,javascript调用c++的接口并专注于业务开发,这样岂不两全其美。 事实上真有这样做的,比如Chromium浏览器,浏览器本身是用c++编写的,提供了很多extension api,然后我们可以用javascript调用这些extension api制作一些extension扩展程序,为浏览器提供更多的功能。

extension api

Chrome浏览器提供了很多extension api,打开开发者工具,在Console里面输入window.chrome,如下图所示: create new chromium extension api

我们在Console输入以下javascript代码:

chrome.tabs.create(
  {"url":"https://www.baidu.com","selected":true},
  function(tab){
    console.log(tab.id);
  }
);

浏览器会在一个新标签页打开https://www.baidu.com,并调用我们传给chrome.tabs.create的会调函数,如下图所示: create new chromium extension api

新增extension api

虽然Chromium已经提供了很多extension api,有时候我们会出于各种目的,会想增加自己的extension api。我就添加一个gclxry的api,来描述如何操作。

增加api feature定义

我们在chrome\common\extensions\api_api_features.json或者extensions\common\api_api_features.json文件里面增加gclxry的api feature定义。这两个目录下面的_api_features.json的差异是:chrome目录下的api与浏览器的业务更加紧密而extensions目录下的api则是更加独立的javascript api。

我们在chrome\common\extensions\api_api_features.json里面增加以下内容:

  "gclxry": {
    "matches": ["<all_urls>"],
    "channel": "stable",
    "extension_types": "all",
    "contexts": "all"
  },

gclxry就是我们api的名字,这个对象有几个字段,具体可以参考我之前写的博客http://blog.gclxry.com/chrome-extension-features:

从gcxlry api feature定义可以看出来,我们把这个api的权限放的很开,这样是为了方便测试。实际开发中,我们不应该开放这么大的权限。 另外Chromium提供的api权限,更多的是定义到_permission_features.json文件里面。我们把权限都定义到_api_features.json文件里面,也是为了简单。

增加api接口定义

api接口的定义就是定义api的参数和返回值。这里有两种定义格式:

可惜json和idl格式Chromium都没有提供完整的文档描述如何使用。不过我们看看chrome\common\extensions\api和extensions\common\api里面其他api接口的json和idl文件定义文件,大概学学就知道了。 我们在chrome\common\extensions\api目录里添加一个gclxry.idl文件,其内容如下:

namespace gclxry {
  callback Callback = void (DOMString result);

  interface Functions {
    static void hello(DOMString name, optional Callback callback);
  };
};

这样我们就定义了一个chrome.gclxry的js对象。它有一个函数hello,接受接受一个字符串的参数并又一个可选的回调函数参数。 然后把gclxry.idl文件加入Build.gn工程配置文件里。

添加api的c++实现代码

我们在chrome\browser\extensions\api\gclxry目录里添加代码文件gclxry_api.h,gclxry_api.cc,并加入到Build.gn工程配置文件里。 gclxry_api.h文件的内容如下:

#ifndef CHROME_BROWSER_EXTENSIONS_API_BROWSER_GCLXRY_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_BROWSER_GCLXRY_API_H_

#include "chrome/browser/extensions/chrome_extension_function.h"

namespace extensions {
namespace api {

class GclxryHelloFunction : public ChromeUIThreadExtensionFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("gclxry.hello", GCLXRY_HELLO)

 protected:
  ~GclxryHelloFunction() override;

  ResponseAction Run() override;
};

}  // namespace api
}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_BROWSER_GCLXRY_API_H_

gclxry_api.cc文件的内容如下:

#include "chrome/browser/extensions/api/gclxry/gclxry_api.h"

#include "chrome/common/extensions/api/gclxry.h"

namespace extensions {
namespace api {

GclxryHelloFunction::~GclxryHelloFunction() {}

ExtensionFunction::ResponseAction GclxryHelloFunction::Run() {
  std::unique_ptr<gclxry::Hello::Params> params(
      gclxry::Hello::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  std::string error("hello error message");
  std::string data = std::string("hello ") + params->name;
  std::unique_ptr<base::ListValue> result(gclxry::Hello::Results::Create(data));
  if (!result)
    return RespondNow(Error(error));

  return RespondNow(ArgumentList(std::move(result)));
}

}  // namespace api
}  // namespace extensions

GclxryHelloFunction 类就是chrome.gclxry.hello函数的实现。

此外还要在extension_function_histogram_value.h里面添加一个GCLXRY_HELLO。

测试新增的api

我们打开个设置页面,这个设置页面的javascript context类型是webui。打开console,输入:

我们打开个设置页面,这个设置页面的javascript context类型是webui。打开console,输入:
chrome.gclxry.hello(
  'world',
  function say_hello(data){
    console.log(data);
  }
);

效果如下图: create new chromium extension api

然后我们打开https://www.baidu.com/,打开开发者工具,在console里面chrome却没有gclxry对象,原因是extension api默认只能在extension_service_worker、blessed_extension、unblessed_extension、content_script、webui这几种javascript context里面存在。而一般的网页的javascript context是web_page类型。因此我们需要在Dispatcher::UpdateBindingsForContext里面添加如下两行代码: create new chromium extension api

参考:http://dev.chromium.org/developers/design-documents/extensions/proposed-changes/creating-new-apis