libcef-JavaScript与C++通信-原理与实现-文档原文
文章目录
- 1.介绍
- 2.JS执行
- 3.窗口绑定
- 4.扩展
- 5.JS的基本类型
- 5.1.JS数组
- 5.2.JS对象
- 5.3.具有访问器的对象
- 5.4.JS函数
- 6.函数和窗口绑定
- 7.功能和扩展
- 8.上下文的使用
- 9.执行函数
- 9.1.使用JS回调
- 10.抛出异常
1.介绍
Chromium和CEF使用V8 JavaScript引擎来实现内部JavaScript (JS)。浏览器中的每个框架都有自己的JS上下文,为在该框架中执行的JS代码提供作用域和安全性(更多信息请参阅“使用上下文”部分)。CEF为客户端应用程序的集成提供了大量的JS特性。
在CEF3中,Blink (WebKit)和JS的执行在一个单独的渲染程序中运行。渲染器进程中的主线程被标识为TID_RENDERER,所有V8的执行都必须在这个线程上进行。与JS执行相关的回调是通过CefRenderProcessHandler接口公开的。当一个新的渲染器进程初始化时,通过CefApp::GetRenderProcessHandler()获取该接口。
在浏览器和呈现程序之间通信的JS api应该使用异步回调来设计。有关更多信息,请参阅GeneralUsage wiki页面的“异步JavaScript绑定”部分。
2.JS执行
从客户端应用程序执行JS的最简单方法是使用CefFrame::ExecuteJavaScript()函数。这个函数在浏览器进程和渲染器进程中都可用,并且可以在JS上下文之外安全地使用。
CefRefPtr<CefBrowser> browser = ...;
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');",frame->GetURL(), 0);
上面的例子会引起alert(‘ExecuteJavaScript works!’);在浏览器的主框架中执行。ExecuteJavaScript()函数可用于与框架JS上下文中的函数和变量交互。为了从JS返回值到客户端应用程序,可以考虑使用窗口绑定或扩展。
3.窗口绑定
窗口绑定允许客户端应用程序将值附加到框架的窗口对象。窗口绑定是使用CefRenderProcessHandler::OnContextCreated()方法实现的。
void MyRenderProcessHandler::OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {// Retrieve the context's window object.CefRefPtr<CefV8Value> object = context->GetGlobal();// Create a new V8 string value. See the "Basic JS Types" section below.CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");// Add the string to the window object as "window.myval". See the "JS Objects" section below.object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
然后,框架中的JavaScript可以与窗口绑定交互。
<script language="JavaScript">
alert(window.myval); // Shows an alert box with "My Value!"
</script>
每当一个帧被重新加载时,窗口绑定都会被重新加载,这样客户端应用程序就有机会在必要时更改绑定。例如,不同的帧可以通过修改绑定到该帧的窗口对象的值来访问客户端应用程序中的不同特性。
4.扩展
扩展类似于窗口绑定,只不过它们被加载到每一帧的上下文中,一旦加载就不能修改。加载扩展时,DOM不存在,在扩展加载期间试图访问DOM将导致崩溃。扩展是使用CefRegisterExtension()函数注册的,该函数应该从CefRenderProcessHandler::OnWebKitInitialized()方法调用。
void MyRenderProcessHandler::OnWebKitInitialized() {// Define the extension contents.std::string extensionCode ="var test;""if (!test)"" test = {};""(function() {"" test.myval = 'My Value!';""})();";// Register the extension.CefRegisterExtension("v8/test", extensionCode, NULL);
}
extensionCode表示的字符串可以是任何有效的JS代码。然后,框架中的JS可以与扩展代码交互。
<script language="JavaScript">
alert(test.myval); // Shows an alert box with "My Value!"
</script>
5.JS的基本类型
CEF支持基本JS数据类型的创建,包括undefined, null, bool, int, double, date和string。这些类型是使用CefV8Value::Create*()静态方法创建的。例如,要创建一个新的JS字符串值,使用CreateString()方法。
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
基本值类型可以在任何时候创建,并且最初并不与特定的上下文相关联(有关更多信息,请参阅“使用上下文”部分)。要测试值类型,请使用Is*()方法。
CefRefPtr<CefV8Value> val = ...;
if (val.IsString()) {// The value is a string.
}
要检索值,请使用Get* value()方法。
CefString strVal = val.GetStringValue();
5.1.JS数组
数组是使用CefV8Value::CreateArray()静态方法创建的,该方法接受一个长度参数。数组只能在上下文中创建和使用(有关更多信息,请参阅“使用上下文”一节)。
// Create an array that can contain two values.
CefRefPtr<CefV8Value> arr = CefV8Value::CreateArray(2);
使用SetValue()方法变量将值赋给数组,该方法变量以索引作为第一个参数。
// Add two values to the array.
arr->SetValue(0, CefV8Value::CreateString("My First String!"));
arr->SetValue(1, CefV8Value::CreateString("My Second String!"));
要测试CefV8Value是否是数组,请使用IsArray()方法。要获取数组的长度,请使用GetArrayLength()方法。要从数组中获取值,请使用GetValue()变量,该变量以索引作为第一个参数。
5.2.JS对象
对象是使用CefV8Value::CreateObject()静态方法创建的,该方法接受一个可选的CefV8Accessor参数。对象只能在上下文中创建和使用(有关更多信息,请参阅“使用上下文”部分)。
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(NULL);
使用SetValue()方法变量将值赋给对象,该方法变量以键字符串作为第一个参数。
obj->SetValue("myval", CefV8Value::CreateString("My String!"));
5.3.具有访问器的对象
对象可以有一个相关联的CefV8Accessor,该CefV8Accessor提供了获取和设置值的本机实现。
CefRefPtr<CefV8Accessor> accessor = …;
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor);
客户端应用程序必须提供的CefV8Accessor接口的实现。
class MyV8Accessor : public CefV8Accessor {public:MyV8Accessor() {}virtual bool Get(const CefString& name,const CefRefPtr<CefV8Value> object,CefRefPtr<CefV8Value>& retval,CefString& exception) OVERRIDE {if (name == "myval") {// Return the value.retval = CefV8Value::CreateString(myval_);return true;}// Value does not exist.return false;}virtual bool Set(const CefString& name,const CefRefPtr<CefV8Value> object,const CefRefPtr<CefV8Value> value,CefString& exception) OVERRIDE {if (name == "myval") {if (value->IsString()) {// Store the value.myval_ = value->GetStringValue();} else {// Throw an exception.exception = "Invalid value type";}return true;}// Value does not exist.return false;}// Variable used for storing the value.CefString myval_;// Provide the reference counting implementation for this class.IMPLEMENT_REFCOUNTING(MyV8Accessor);
};
为了将值传递给访问器,必须使用接受AccessControl和PropertyAttribute参数的SetValue()方法变体进行设置。
obj->SetValue("myval", V8_ACCESS_CONTROL_DEFAULT, V8_PROPERTY_ATTRIBUTE_NONE);
5.4.JS函数
CEF支持使用本地实现创建JS函数。函数是使用CefV8Value::CreateFunction()静态方法创建的,该方法接受name和CefV8Handler参数。函数只能在上下文中创建和使用(有关更多信息,请参阅“使用上下文”部分)。
CefRefPtr<CefV8Handler> handler = …;
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
必须由客户机应用程序提供的CefV8Handler接口的实现。
class MyV8Handler : public CefV8Handler {public:MyV8Handler() {}virtual bool Execute(const CefString& name,CefRefPtr<CefV8Value> object,const CefV8ValueList& arguments,CefRefPtr<CefV8Value>& retval,CefString& exception) OVERRIDE {if (name == "myfunc") {// Return my string value.retval = CefV8Value::CreateString("My Value!");return true;}// Function does not exist.return false;}// Provide the reference counting implementation for this class.IMPLEMENT_REFCOUNTING(MyV8Handler);
};
6.函数和窗口绑定
函数可用于创建复杂的窗口绑定。
void MyRenderProcessHandler::OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {// Retrieve the context's window object.CefRefPtr<CefV8Value> object = context->GetGlobal();// Create an instance of my CefV8Handler object.CefRefPtr<CefV8Handler> handler = new MyV8Handler();// Create the "myfunc" function.CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);// Add the "myfunc" function to the "window" object.object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
<script language="JavaScript">
alert(window.myfunc()); // Shows an alert box with "My Value!"
</script>
7.功能和扩展
函数可以用来创建复杂的扩展。注意使用“本机函数”前向声明,这在使用扩展时是必需的。
void MyRenderProcessHandler::OnWebKitInitialized() {// Define the extension contents.std::string extensionCode ="var test;""if (!test)"" test = {};""(function() {"" test.myfunc = function() {"" native function myfunc();"" return myfunc();"" };""})();";// Create an instance of my CefV8Handler object.CefRefPtr<CefV8Handler> handler = new MyV8Handler();// Register the extension.CefRegisterExtension("v8/test", extensionCode, handler);
}
<script language="JavaScript">
alert(test.myfunc()); // Shows an alert box with "My Value!"
</script>
8.上下文的使用
浏览器窗口中的每一帧都有自己的V8上下文。上下文定义了在该框架中定义的所有变量、对象和函数的作用域。如果当前代码位置在调用堆栈的更高位置有CefV8Handler、CefV8Accessor或OnContextCreated()/ oncontexttremonitored()回调,V8将位于上下文中。
OnContextCreated()和oncontexttreleasing()方法定义了与一个框架相关的V8上下文的完整生命周期。在使用这些方法时,请注意遵循以下规则:
不要在调用V8上下文的oncontextreleasing()之后保留或使用V8上下文引用。
所有V8对象的生命周期都是未指定的(直到GC)。在维护直接从V8对象到自己内部实现对象的引用时要小心。在很多情况下,使用你的应用程序与V8上下文关联的代理对象可能会更好,当上下文调用oncontextreleasing()时,它可以被“断开连接”(允许你的内部实现对象被释放)。
如果V8当前不在上下文中,或者需要检索和存储对上下文的引用,可以使用两个可用的CefV8Context静态方法之一。GetCurrentContext()返回当前正在执行JS的帧的上下文。GetEnteredContext()返回JS开始执行的帧的上下文。例如,如果frame1中的一个函数调用了frame2中的一个函数,那么当前上下文将是frame2,而输入的上下文将是frame1。
数组、对象和函数只能被创建、修改,如果V8是在上下文中,对于函数,则只能被执行。如果V8不在上下文中,那么应用程序需要通过调用enter()进入上下文,并通过调用exit()退出上下文。Enter()和Exit()方法只能使用:
在现有上下文之外创建V8对象、函数或数组时。例如,当创建一个JS对象以响应本地菜单回调时。
在当前上下文以外的上下文中创建V8对象、函数或数组时。例如,如果来自frame1的调用需要修改frame2的上下文。
9.执行函数
本机代码可以通过使用ExecuteFunction()和ExecuteFunctionWithContext()方法来执行JS函数。ExecuteFunction()方法只能在V8已经在上下文中使用,如“使用上下文”部分所述。ExecuteFunctionWithContext()方法允许应用程序指定将要输入执行的上下文。
9.1.使用JS回调
当用本地代码注册JS函数回调时,应用程序应该在本地代码中存储对当前上下文和JS函数的引用。这可以按如下方式实现。
- 在OnJSBinding()中创建一个“register”函数。
void MyRenderProcessHandler::OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {// Retrieve the context's window object.CefRefPtr<CefV8Value> object = context->GetGlobal();CefRefPtr<CefV8Handler> handler = new MyV8Handler(this);object->SetValue("register",CefV8Value::CreateFunction("register", handler),V8_PROPERTY_ATTRIBUTE_NONE);
}
- 在“register”函数的MyV8Handler::Execute()实现中,保持对上下文和函数的引用。
bool MyV8Handler::Execute(const CefString& name,CefRefPtr<CefV8Value> object,const CefV8ValueList& arguments,CefRefPtr<CefV8Value>& retval,CefString& exception) {if (name == "register") {if (arguments.size() == 1 && arguments[0]->IsFunction()) {callback_func_ = arguments[0];callback_context_ = CefV8Context::GetCurrentContext();return true;}}return false;
}
- 通过JavaScript注册JS回调。
<script language="JavaScript">
function myFunc() {// do something in JS.
}
window.register(myFunc);
</script>
- 在以后的某个时间执行JS回调。
CefV8ValueList args;
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Exception> exception;
if (callback_func_->ExecuteFunctionWithContext(callback_context_, NULL, args, retval, exception, false)) {if (exception.get()) {// Execution threw an exception.} else {// Execution succeeded.}
}
有关使用回调的更多信息,请参阅GeneralUsage wiki页面的异步JavaScript绑定部分。
10.抛出异常
如果在CefV8Value::ExecuteFunction*()之前调用CefV8Value::SetRethrowExceptions(true),则V8在函数执行期间生成的任何异常都将立即被重新抛出。如果一个异常被重新抛出,任何本地代码都需要立即返回。只有当在调用堆栈中有一个更高的JS调用时,才应该重新抛出异常。例如,考虑以下调用堆栈,其中“JS”是一个JS函数,“EF”是一个本地ExecuteFunction调用:
堆栈1:JS1 ->EF1→JS2→EF2
堆栈2:本地菜单->EF1→JS2→EF2
在堆栈1中,EF1和EF2的重抛出都应该为真。对于堆栈2,重抛出EF1为假,EF2为真。
这可以通过在本机代码中有两种EF调用站点来实现:
只能从V8处理器调用。这包括堆栈1中的ef1和EF2以及堆栈2中的EF2。重新投掷总是正确的。
只在本地调用。这包括堆栈2中的EF1。重新投掷总是假的。
在重新抛出异常时要非常小心。不正确的使用(例如,在异常被重新抛出后立即调用ExecuteFunction())可能会导致应用程序崩溃或以难以调试的方式出现故障。
libcef-JavaScript与C++通信-原理与实现-文档原文相关推荐
- js从服务器获取word文档,javascript - 使用Office.js API将Word文档(.docx)保存到后端服务器 - 堆栈内存溢出...
我在将byte数组(使用Office.js从Microsoft Office的任务窗格中获取)保存到Word文档文件(在服务器端)时遇到了一些麻烦. 这就是我在做什么: 我正在使用此库获取Word文档 ...
- 支付宝接口(扫码支付的原理)使用文档说明 支付宝异步通知(notify_url)与return_url.
支付宝接口使用文档说明 支付宝异步通知(notify_url)与return_url. 现支付宝的通知有两类. A服务器通知,对应的参数为notify_url,支付宝通知使用POST方式 B页面跳 ...
- 分布式搜索引擎01-elasticsearch-介绍、倒排索引原理、概念(文档和字段,索引和映射)、安装、索引库crud、文档crud、RestAPI(java代码实现es的crud)
文章目录 分布式搜索引擎01 0.学习目标 1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 1.1.2.ELK技术栈 1.1.3.elasticse ...
- huaweicloud华为云-云通信-语音通话对接文档整理
语音回呼业务体验流程 用户Jack在浏览网站时,通过应用软件呼叫客服人员,呼叫请求上报至RTC业务平台.RTC业务平台分别呼叫主叫Jack和客服Sophia,使主叫和被叫互相通话.详细体验过程如下图所 ...
- html文档中的元数据,javascript – 如何使用pdf.js从pdf文档中获取元数据
仅使用没有第三方查看器的PDF.js库,您可以使用promises获取这样的元数据. PDFJS.getDocument(url).then(function (pdfDoc_) { pdfDoc = ...
- MAX3222/MAX3232/ MAX3237/MAX3241/串口通信中文_技术文档
原文档下载地址:https://download.csdn.net/download/britripe/10688877 去MAX公司也应该可以自己下!
- 写word文档时计算机的工作原理,写word文档时还没保存电脑就死机如何恢复
电脑已经成为人类生活中不可缺少的一部分,人们利用电脑可以轻松的完成以前需要大量劳动力才能完成的工作,但在人们在使用电脑中也会遇到很多的麻烦,比如死机,这些原因烦恼着每一个电脑用户,这不,最近就有小伙伴 ...
- JavaScript 运行机制及原理
写js也有两年多了,一直对它的运行机制和原理不是很了解,今天特意把大神们的理论和自己的总结都记录到下面: 什么是JavaScript解析引擎 简单地说,JavaScript解析引擎就是能够" ...
- React-Native系列Android——Native与Javascript通信原理(一)
React-Native最核心的是Native与Javascript之间的通信,并且是双向通信.Native层到Javascript层,Javascript层到Native层.虽说是两个方向,但实现上 ...
最新文章
- C# Stream 和 byte[] 之间的转换
- 如何利用CSS给同一个网页中的超链接设置设置不同的样式?
- tomcat配置虚拟目录,虚拟目录,tomcat目录,tomcat服务器,网站图片虚拟目录
- Navicat mysql 加索引_mysql 索引 (Navicat添加索引)
- 浙江省第二届大学生网络与信息安全竞赛在线预赛
- 首期openGauss训练营结营,48个FAQ和全部PPT通通给你,随附62人结营学员名单
- 2018-03-28 Linux学习
- Wanna Be a Pragmatic Programmer
- Eclipse中的Web项目自动部署到Tomcat
- matlab朴素贝叶斯手写数字识别_「深度学习系列」PaddlePaddle之手写数字识别
- python加法运算符_python 入门之 – 基本运算符(七)
- 京东下单接口sdk java,Flutter 插件开发-接入京东SDK唤醒(ios篇)
- 金蝶kis商贸采购单商品代码_金蝶KIS商贸版操作明细
- Mybatis_select、insert、update、delete常用属性
- Win11 2022 Edge浏览器解决教资报名(浏览器不兼容)问题
- 石家庄地铁查询(双人项目)
- 计算机的软键盘在哪里,如何调出软键盘_怎么在电脑上调出软键盘_如何调出搜狗软键盘-Guide信息网...
- 1-11摇号机java_11选5在线模拟摇号
- 使用vs2019用c++创建dll库
- 【Introduction】人类大脑