在Chromium里的Blink中添加API
我的目标导出js的接口使得可以在页面通过js调用C++的一些方法完成任务
前言
关于导出C++接口方法
1 在extension api导出接口方便extension调用
2 在Blink内核中导出接口
我使用方法2完成导出,阅读一下内容可能需要了解Web IDL,Blink GC,Chromium Task等方面的知识,相关的一些资料在最后给出
环境
chroumium版本 70.0.3538.513
过程
在C++中定义Example类,该类会执行一个耗时操作,完成后调用回调将结果返回
在js里需要构造Example类,添加一个回调函数,然后调用将结果输出
添加目录
在third_party/blink/renderer/modules添加example目录,idl及对应的C++的实现统一放在这个目录
编写IDL文件
1 WebIDL是一种接口描述规范,可以用来生成一些C++和js之间的转换代码
2 inishedCallback为回调限制的类型,编译时会自动生成一个C++对象V8FinishedCallback与之对应
3 [Constructor]表明该interface可以再js中使用new构造,该属性可以有参数,对应的C++方法为static Example* Create(),Example实例被blink GC管理不需要对应的delete
4 Example需要编写blink::Example与之对应
1 2 3 4 5 6 7 8
| callback FinishedCallback = void(DOMString msg);
[Constructor] interface Example { boolean Start(); DOMString Name(); void OnFinish(FinishedCallback callback); };
|
编写C++代码
1 继承ScriptWrappable类可以提供类型描述,并且该类继承GarbageCollectedFinalized,所以Example是一个blink GC对象.类Example声明的方法与idl是对应的,除了构造函数比较特别以外.
2 #include "third_party/blink/renderer/bindings/modules/v8/v8_finished_callback.h"这个文件是根据idl里面callback自动生成的文件,编译后才会有.
3 不要再别的线程里调用js注册的回调函数
以上说明在代码注释里面也有对应
example.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #ifndef __EXAMPLE_H__ #define __EXAMPLE_H__ #include <thread> #include "base/task/post_task.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_finished_callback.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
// 类Example需要放在blink空间里 namespace blink {
// 类需要继承ScriptWrappable提供类型信息 // ScriptWrappable是一个blink GC管理的类不可以直接delete class Example final : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO();
public: // js调用new时调用该函数,相当于对外导出的构造函数 static Example* Create() { return new Example; } bool Start() { return base::PostTaskAndReply( FROM_HERE, base::BindOnce(&Example::startWork, base::Unretained(this)), base::BindOnce(&Example::finished, base::Unretained(this))); }
// String与idl文件中DOMString对应 String Name() { return String("Example"); }
void startWork() { std::this_thread::sleep_for(std::chrono::seconds(10)); }
void finished() { if (this->callback_) { // 调用js中的回调 this->callback_->InvokeAndReportException(nullptr, String("task finished")); } }
void OnFinish(V8FinishedCallback* callback) { callback_ = callback; }
// 因为该类包含需要追踪的类Member<V8FinishedCallback>,该方法必须实现 void Trace(Visitor* visitor) override { visitor->Trace(callback_); ScriptWrappable::Trace(visitor); }
~Example() override = default;
private: // 保存js注册的回调 Member<V8FinishedCallback> callback_; };
} // namespace blink
#endif
|
example.cc
编写编译文件
修改的地方比较杂,但是不难理解
1 在third_party/blink/renderer/modules/example里添加以下文件来说明example模块编译的规则
BUILD.gn
1 2 3 4 5 6 7 8 9 10
| import("//third_party/blink/renderer/modules/modules.gni")
blink_modules_sources("example") { sources = [ "example.cc", "example.h", ] }
|
2 在third_party\blink\renderer\modules\BUILD.gn文件的deps变量里添加以下内容确保example模块被编译
1
| "//third_party/blink/renderer/modules/example",
|
3 在third_party\blink\renderer\modules\modules_idl_files.gni文件的modules_idl_files里的get_path_info传入参数中添加以下内容确保idl文件被编译
4 在third_party\blink\renderer\bindings\modules\v8\generated.gni文件的generated_modules_callback_function_files变量里添加以下内容确保idl生成的C++文件被编译
1 2
| "$bindings_modules_v8_output_dir/v8_finished_callback.cc", "$bindings_modules_v8_output_dir/v8_finished_callback.h",
|
验证
chromium编译成功后在console输入以下语句成功后会在10秒后弹出消息
1 2 3
| example = new Example() example.OnFinish((msg)=>{alert(msg)}) example.Start()
|
参考资料
https://heycam.github.io/webidl/#idl-types
https://www.chromium.org/developers/web-idl-interfaces
https://www.chromium.org/blink/webidl#TOC-Style
https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md#GarbageCollected