CEF:C++和JS交互

C++和JS交互V8原理:https://github.com/fanfeilong/cefutil/blob/master/doc/content_register_v8_extension.md

CEF一个页面的框架如下图所示:

在这里插入图片描述

CefBrowser:一个普通的浏览器页面(HTML)

CefFrame:每一个页面都由至少一个frame组成,最顶层的为mainframe

context:JS执行环境,每个frame都有自己独立的context,CEF中使用V8JavaScriptEngine解析和执行JS代码

CEF 中的JavaScript

  • CEF 利用 V8 JS 引擎来实现 JS。
  • 浏览器中的每一个 frame 都有自己的 JS 上下文,JS 只能在该上下文中执行。
  • JS 只能在渲染进程中的 TID_RENDERER 线程中执行。
  • 有关 JS 回调的接口都包含在 CefRenderProcessHandler 中,因此我们要实现这个接口来对 JS 进行扩展。这个接口一般由 CefApp 实现。

C++调用JS

事实上cef本身支持直接执行js(js代码或者js函数)的接口,原型如下:

  ///
  // Execute a string of JavaScript code in this frame. The |script_url|
  // parameter is the URL where the script in question can be found, if any.
  // The renderer may request this URL to show the developer the source of the
  // error.  The |start_line| parameter is the base line number to use for error
  // reporting.
  ///
  /*--cef(optional_param=script_url)--*/
  virtual void ExecuteJavaScript(const CefString& code,
                                 const CefString& script_url,
                                 int start_line) = 0;

  ///class CefV8Value,此时可以获取返回值
  // Execute the function using the current V8 context. This method should only
  // be called from within the scope of a CefV8Handler or CefV8Accessor
  // callback, or in combination with calling Enter() and Exit() on a stored
  // CefV8Context reference. |object| is the receiver ('this' object) of the
  // function. If |object| is empty the current context's global object will be
  // used. |arguments| is the list of arguments that will be passed to the
  // function. Returns the function return value on success. Returns NULL if
  // this method is called incorrectly or an exception is thrown.
  ///
  /*--cef(optional_param=object)--*/
  virtual CefRefPtr<CefV8Value> ExecuteFunction(
      CefRefPtr<CefV8Value> object,
      const CefV8ValueList& arguments) = 0;
1.ExecuteJavaScript 可以直接执行JS函数,但是没有办法获取返回值。
//@param js js代码或者函数,
//eg:js="add(1,2)"
void CBrowserHandler::ExeJs(const CefString &js)
{
	...
	browser->GetMainFrame()->ExecuteJavaScript(js, L"", 0);
	...
}

#######################################C++调用JS测试,直接通过ExeJs("add(10,20)")调用JS函数####################
//JS:
function call_add(val1,val2)
{
	alert(window.add(val1,val2));
}

//C++
...
if (m_browser_app)
	m_browser_app->ExeJs(L"call_add(10,20)");
...

在这里插入图片描述

2、ExecuteFunction需要配合V8Context一起执行。执行之前需要context->Enter(),完成之后context->Exit()

V8Context分为两种情况:

  1. 使用自己保存的V8Context (Render进程使用)
  2. 使用全局的V8Context (Render进程使用)
//获取局部保存的Context,并且执行Js函数
CefRefPtr<CefV8Context> context = m_map_call_back[iMsgID].first;
CefRefPtr<CefV8Value> call_back = m_map_call_back[iMsgID].second;
if (context && call_back)
{
	context->Enter();
	if (context->GetBrowser())
	{
		CefV8ValueList call_arguments;
		call_arguments.push_back(CefV8Value::CreateString(args->GetString(2)));
		if (call_back->IsFunction())
			call_back->ExecuteFunction(NULL, call_arguments);
	}
	context->Exit();
}

//获取全局的JS函数并且执行GetV8Context只能在render进程中使用
CefRefPtr<CefV8Context> context = browser->GetMainFrame()->GetV8Context();
context->Enter();
CefRefPtr<CefV8Value> global = context->GetGlobal();
if (global)
{
	//获取全局函数js_handler_call_back,并且执行js_handler_call_back
	CefRefPtr<CefV8Value> call_back = global->GetValue("js_handler_call_back");
	if (call_back && call_back->IsFunction())
	{
		CefV8ValueList call_arguments;
		call_arguments.push_back(CefV8Value::CreateString(args->GetString(2)));
		if (call_back->IsFunction())
			call_back->ExecuteFunction(NULL, call_arguments);
	}
}
context->Exit();

本次demo中,我们预定如果设置回调,则第一个参数必须是函数指针,否则我们将使用默认的全局回调函数js_handler_call_back回调数据。

	function register(){
		MyMath.register(call_back);
	}
	
	function MyTimer(){
		MyMath.Timer(1);
	}

	<p onclick="register()">this is a register function</p>
	<button onclick="MyTimer()">启动Timer(1)</button>

在这里插入图片描述

JS调用C++(Render进程实现)

绑定值和函数到Window对象

在CefRenderProcessHandler::OnContextCreated() 中绑定一些值给 JS 的 window 对象。

class CRenderApp :public CefApp, public CefRenderProcessHandler
void CRenderApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
	CefRefPtr<CefFrame> frame,
	CefRefPtr<CefV8Context> context)
{
 	CEF_REQUIRE_RENDERER_THREAD();
 	
	//添加全局成员window的成员
	CefRefPtr<CefV8Value> object = context->GetGlobal();
	CefRefPtr<CefV8Value> v8Value = CefV8Value::CreateString("c++ value");
	object->SetValue("MyValue", v8Value, V8_PROPERTY_ATTRIBUTE_NONE);

	//添加全局函数,add函数实现在CJSHandler里面
	CefRefPtr<CefV8Handler> add_handler = new CJSAdd(this);
	CefRefPtr<CefV8Value> add_func = CefV8Value::CreateFunction("add", add_handler);
	object->SetValue("add", add_func, V8_PROPERTY_ATTRIBUTE_NONE);
	//此时支持window.MyValue 属性,和window.add函数
}

bool CJSAdd::Execute(const CefString& name,CefRefPtr<CefV8Value> object,const CefV8ValueList& arguments,CefRefPtr<CefV8Value>& retval,CefString& exception)
{
	//function add
	if (name == "add")
	{
		int32  val2 = arguments[0]->GetIntValue();
		int32  val1 = arguments[1]->GetIntValue();
		int32 sum = val1 + val2;
		retval = CefV8Value::CreateInt(sum);
		return true;
	}
	return false;
}

#######################################JS测试,直接通过window.MyValue访问####################
<script>
function show(){
	alert(window.MyValue);
}

function call_add(val1,val2){
	alert(window.add(val1,val2));
}
</script>

在这里插入图片描述
在这里插入图片描述

扩展JS

在 CefRenderProcessHandler::OnWebKitInitialized() 中将 JS 脚本注入到 V8 中。
只能被Render 进程的调用

扩展值和函数

更详细的demo可以参考CefRegisterExtension 类的说明

void CRenderApp::OnWebKitInitialized()
{
	CEF_REQUIRE_RENDERER_THREAD();

	// Define the extension contents.
	std::string extensionCode =
		"var MyMath;"
		"if (!MyMath)"//如果没有MyMath对象,则定义一个MyMath对象
		"  MyMath = {};"
		"(function() {"
		"  MyMath.add = function(num1, num2) {"//定义一个MyMath的add函数,参数顺序为外部js调用顺序
		"    native function add();"//native 函数,不带参数
		"    return add(num1, num2);"//add在CefV8Handler具体实现,参数顺序为CefV8Handler 中的参数顺序
		"  };"
		"  MyMath.hellow = 'Hello JS!';"//定义一个MyMath的成员变量
		"})();";

	CefRefPtr<CefV8Handler> add_handler = new CJSAdd();
	// Register the extension.
	CefRegisterExtension("v8/test", extensionCode, add_handler);
}

###################JS测试,直接通过MyMath.add调用函数,MyMath.hellow获取成员变量####################
function CallMyMathAdd()
	{
		v1 = document.getElementById('num1').value;
		v2 = document.getElementById('num2').value;
		alert(MyMath.add(Number(v1),Number(v2)));
	}
	
	function ShowMyMathHellow()
	{
		alert(MyMath.hellow);
	}

在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值