CEF中C++与JS交互

花了几天时间学习了下C++和JS交互,写下笔记记录下 ,也欢迎大神指教

https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md#markdown-header-js-functions

上记录了三种C++与JS交互方式,以下我来讨论下。

首先引用下博客中的关于CEF架构的图

参考博客:

https://blog.csdn.net/foruok/article/details/50573612

https://blog.csdn.net/chenlycly/article/details/53355670

https://blog.csdn.net/swimming_in_it_/article/details/78869549

1. C++调用JS

1)ExecuteJavaScript

最简单的C++调用JS方法,需要Browser的对象,我这里调用一般是在CefClient的实现类中使用,但是有个缺点就是执行函数获得不了返回值。

void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
                                  const CefString& title) {
  CEF_REQUIRE_UI_THREAD();

  CefRefPtr<CefFrame> frame = browser->GetMainFrame();
  frame->ExecuteJavaScript("sayHello()", frame->GetURL(), 0);

这里是在OnTitleChange调用ExecuteJavaScript,调用sayHello函数是在JS代码里声明的。

该类的声明是:

class SimpleHandler : public CefClient,
                      public CefDisplayHandler,
                      public CefLifeSpanHandler,
                      public CefLoadHandler

而OnTitleChange是SimpleHandler重写CefDisplayHandler里的虚函数,在标题改变时调用OnTitleChange函数。

JS代码:

<script language = "JavaScript">
	document.title = 'test js C++ communication';

	window.setTimeout(function() {
		document.title = 'C++ and JS';
	}, 5000);

	function sayHello() {
		alert("Hello!");
	}
</script>

JS代码里设置过5秒后修改标题,然后在OnTitleChange函数调用sayHello()

 

2. JS调用C++

1) Window Binding

这种方式就是在C++里声明的对象或函数绑定到JS中的window对象上,这样在JS代码使用window对象调用。

这种需要在一个类继承CefApp和CefRenderProcessHandler,官方建议重新写一个类,而不是与继承

CefBrowserProcessHandler写成同一个类,具体情况我也不知道,但是CefRenderProcessHandler的函数是在一个进程,

CefBrowserProcessHandler的函数是另一个进程,而我试试同时继承两个类(SimpleApp),然后在CefInitialize启动这各类

SimpleApp对象, 但是继承自CefRenderProcessHandler的函数是没有在执行,具体还可以请大神赐教。然后我就写了另个类

RenderApp,将原来的SimpleApp改写成BrowerApp,RenderApp继承CefApp和CefRenderProcessHandler,BrowerApp

继承CefApp和CefBrowserProcessHandler中。

我们把重点放在RenderApp,JS交互C++是在RenderApp实现,Window Binding实现方式是在 OnContextCreated,如下:

void RenderApp::OnContextCreated(CefRefPtr<CefBrowser> browser, 
			         CefRefPtr<CefFrame> frame,
				 CefRefPtr<CefV8Context> context)
{
	CefRefPtr<CefV8Value> object = context->GetGlobal();

	CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value");
	object->SetValue("myvalue", str, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction("loadStuList", m_V8Handler);
	object->SetValue("loadStuList", function, V8_PROPERTY_ATTRIBUTE_NONE);
}

使用context获得对象,然后将object获function绑定到这个对象上,V8是一个JS引擎,V8引擎是一个JavaScript引擎实现,最

初由一些语言方面专家设计,后被谷歌收购,随后谷歌对其进行了开源。V8使用C++开发,,在运行JavaScript之前,相比其它

的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用

了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。

V8支持众多操作系统,如windows、linux、android等,也支持其他硬件架构,如IA32,X64,ARM等,具有很好的可移植和跨平

台特性。(见:https://blog.csdn.net/swimming_in_it_/article/details/78869549)。CefV8Value是一个类似的万能类型,

具体可以参考源码。上图是绑定了string对象和function(loadStuList),然后在JS代码里调用就好了。如下:

<script language = "JavaScript">
	document.title = 'test js C++ communication';

	function sayValue() {
		alert(window.myvalue);
	}

	function sayUserName() {
		alert(window.loadStuList('Lily', '123456'));
	}
</script>

只需要在JS里调用sayValue和sayUserName函数就会到C++里执行,那究竟是在C++里怎样执行呢?回到OnContextCreated

函数里,CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction("loadStuList", m_V8Handler)中有

m_V8Handler,所以JS调用C++的函数执行是和m_V8Handler有关,我是这样声明的:

CefRefPtr<ClientV8Handler> m_V8Handler;

ClientV8Handler的声明是:

class ClientV8Handler : public CefV8Handler
{
public:
	virtual bool Execute(const CefString& name,
			     CefRefPtr<CefV8Value> object,
			     const CefV8ValueList& arguments,
			     CefRefPtr<CefV8Value>& retval,
			     CefString& exception) OVERRIDE;

private:
	IMPLEMENT_REFCOUNTING(ClientV8Handler)
};

而调用C++就是在Execute中:

bool ClientV8Handler::Execute(const CefString& name,
	CefRefPtr<CefV8Value> object,
	const CefV8ValueList& arguments,
	CefRefPtr<CefV8Value>& retval,
	CefString& exception)
{


	if (name == "loadStuList") {
		if (arguments.size() == 2) {
			CefString strUser = arguments.at(0)->GetStringValue();
			CefString strPass = arguments.at(1)->GetStringValue();


			retval = CefV8Value::CreateString(strPass);
		}
		else {
			retval = CefV8Value::CreateInt(2);
		}


		return true;
	}

	return false;
}

当JS调用loadStuList时就会到这个函数,retval是loadStuList的返回值等等。

2)Extensions

这种方式很像window binding,不同点是可以将extensions记载到任意一个Frame且不能改变。注意的是加载extensions时

DOM对象还没生成,就是说extensions的代码不能使用DOM对象。该方式使用是在RenderApp的OnWebKitInitialized中实现

void RenderApp::OnWebKitInitialized()
{
	std::string app_code =
		"var app;"
		"if (!app)"
		"    app = {};"
		"(function() {"
		"    app.GetId = function(x) {"
		"        native function GetId(x);"
		"    };"
		"})();";

	CefRegisterExtension("v8app", app_code, m_V8Handler);
}

上边的app_code是js代码,意思是在C++给JS声明,然后在JS中调用就好,以上app_code意思是声明app对象,为该类声明

GetId函数,在JS代码中调用是:

<script language = "JavaScript">
	document.title = 'test js C++ communication';

	function sayExtensions() {
		alert(app.GetId(2));
	}
</script>

在ClientV8Handler实现是:

else if (name == "GetId") {
    CefRefPtr<CefFrame> frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
    frame->ExecuteJavaScript("alert('Hello from extension');", frame->GetURL(), 0);		
		
    retval = CefV8Value::CreateInt(arguments.size());
    return true;
}

写到这里发现有点不对劲,怎么全是JS调用C++, 而C++调用JS只有ExecuteJavaScript,但是拿不到返回值,这很奇怪,不过可

以在JS里设置回调函数,然后在C++里需要的时候调用这个回调函数就能达到C++调用JS了

 

最后,就是启动这个RenderApp就好了,如下:

CefRefPtr<RenderApp> renderApp(new RenderApp);
int exit_code = CefExecuteProcess(main_args, renderApp.get(), sandbox_info);
if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
}

CefExecuteProcess这个函数时用来启动子进程的,所以将RenderApp启动是在这个函数里。
 

下边是我写的代码,希望交流。

https://download.csdn.net/download/leapmotion/10508861

是我从官方例子cefsimple拿来做的一些修改,可以单独跑起来,中间我遇到了问题是在release可以跑起来,但是在debug下就

会有跑起来然后过一会儿退出了一个进程,是少加了manifest文件,具体做法是将官方例子里的

cefclient\resources\compatibility.manifest文件拷贝到工程目录下引入(属性->配置属性->清单工具->输入输出->附加清单

文件->填写".\compatibility.manifest"<没有引号或直接写绝对路径>)。

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页