C++ CEF 谷歌内核 和JS互交

JavaScript023

C++ CEF 谷歌内核 和JS互交,第1张

朋友 ,有结果吗,我也是碰到了这个问题。这个页面JS调用C++,C++调用页面JS一般是要通过com接口的。在IE的webbrowser里我可以调用成功。在cef里还没有好的方法。给个链接共同学习https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration

需要利用回调函数来实现,举例说明:

本例小心翼翼选择的场景是:写作业。(hope you like)

自己写

注:写作业这个动作至少交代三个方面:谁,什么动作(写),写什么。

下面先从(有个学生,写,作业)开始。

# 1. 有个学生

Student student = new Student()

# 2. 该学生有写作业这个动作需要执行

student.doHomeWork(someHomeWork)

# 3. 注意到这个写作业这个动作是需要得到入参“作业”的后才能进行的。所以给这个学生new了个简单的题目做。

String aHomeWork = "1+1=?"

student.doHomeWork(aHomeWork)

至此,完成写作业的动作。

完整代码

public class Student {

public void doHomeWork(String homeWork) {

System.out.println("作业本")

if("1+1=?".equals(homeWork)) {

System.out.println("作业:"+homeWork+" 答案:"+"2")

} else {

System.out.println("作业:"+homeWork+" 答案:"+"不知道~~")

}

}

public static void main(String[] args) {

Student student = new Student()

String aHomeWork = "1+1=?"

student.doHomeWork(aHomeWork)

}

}

程序执行作业本

作业:1+1=? 答案:2

基于CEF开发时经常需要在JS和C++代码间通信,我们在CEF中JavaScript与C++交互中讨论了常见的交互方式,不过都是在Renderer进程中,这次来看看如何在JS和Browser进程间通信,基本介绍可以看这里:

具体做法,上面给出的链接里有大概说明,另外在cef_message_router.h中有详细说明,它分了9步,有需要的可以去查看。我试验了下可行。按照我的实验,从三个方面来说说:

browser进程

renderer进程

html中的js

foruok原创,转载请关注微信订阅号“程序视界”联系foruok。

browser进程

1)配置browser进程这一侧的Message Router

使用CefMessageRouterConfig来定义可以在html中使用的方法名字。代码片段可能如下:

...

CefMessageRouterConfig g_messageRouterConfig

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,

_In_opt_ HINSTANCE hPrevInstance,

_In_ LPTSTRlpCmdLine,

_In_ int nCmdShow)

{

UNREFERENCED_PARAMETER(hPrevInstance)

UNREFERENCED_PARAMETER(lpCmdLine)

// Enable High-DPI support on Windows 7 or newer.

CefEnableHighDPISupport()

g_messageRouterConfig.js_query_function = "cefQuery"

g_messageRouterConfig.js_cancel_function = "cefQueryCancel"

CefMainArgs main_args(hInstance)

...

}

注意我在main函数之外定义了一个CefMessageRouterConfig的实例,然后在main函数中设置了query和cancel这两个函数。它们定义了可以在html的js代码里使用的方法,比如根据上面代码,你可以用window.cefQuery({…})。

2)创建CefMessageRouterBrowserSide的实例

browser进程这边需要一个CefMessageRouterBrowserSide的实例来处理renderer进程发过来的消息。建议把这个实例作为成员变量放在CefClient的派生类中,因为它CefMessageRouterBrowserSide定义了一些方法,需要在指定的地方调用。

声明和初始化的代码片段:

CefRefPtr<CefMessageRouterBrowserSide>m_browser_side_router

...

extern CefMessageRouterConfig g_messageRouterConfig

m_browser_side_router = CefMessageRouterBrowserSide::Create(g_messageRouterConfig)

...

3)调用预定义方法

CefMessageRouterBrowserSide预定义了下列接口,需要在指定地方调用:

// call from CefClient::OnProcessMessageReceived

bool OnProcessMessageReceived(...)

// call from CefLifeSpanHandler::OnBeforeClose()

void OnBeforeClose(CefRefPtr<CefBrowser>browser)

// call from CefRequestHandler::OnRenderProcessTerminated()

void OnRenderProcessTerminated(CefRefPtr<CefBrowser>browser)

// call from CefRequestHandler::OnBeforeBrowse()

void OnBeforeBrowse(...)

所以,我们的这个ClientHandler类,声明如下:

class ClientHandler : public CefClient,

public CefDisplayHandler,

public CefLifeSpanHandler,

public CefLoadHandler,

public CefRequestHandler

{

...

}

ClientHandler要实现CefClient的OnProcessMessageReceived、CefLifeSpanHandler的OnBeforeClose、CefRequestHandler的OnRenderProcessTerminated和OnBeforeBrowse方法,在这些方法里调用CefMessageRouterBrowserSide的同名方法即可。代码片段如下:

void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser>browser)

{

CEF_REQUIRE_UI_THREAD()

m_browser_side_router->OnBeforeClose(browser)

...

}

bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser>browser,

CefProcessId source_process,

CefRefPtr<CefProcessMessage>message)

{

if (m_browser_side_router->OnProcessMessageReceived(browser, source_process, message)) return true

return false

}

bool ClientHandler::OnBeforeBrowse(CefRefPtr<CefBrowser>browser, CefRefPtr<CefFrame>frame, CefRefPtr<CefRequest>request, bool is_redirect)

{

m_browser_side_router->OnBeforeBrowse(browser, frame)

return false

}

void ClientHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser>browser, TerminationStatus status)

{

m_browser_side_router->OnRenderProcessTerminated(browser)

}

4) 在browser进程里实现处理JS查询的handler

CefMessageRouterBrowserSide定义了一个内部类Handler,继承它:

class BrowserSideJSHandler : public CefMessageRouterBrowserSide::Handler

{

public:

BrowserSideJSHandler()

virtual bool OnQuery(CefRefPtr<CefBrowser>browser,

CefRefPtr<CefFrame>frame,

int64 query_id,

const CefString&request,

bool persistent,

CefRefPtr<Callback>callback)

virtual void OnQueryCanceled(CefRefPtr<CefBrowser>browser,

CefRefPtr<CefFrame>frame,

int64 query_id)

}

实现 OnQuery 和 OnQueryCanceled两个方法:

bool BrowserSideJSHandlerwww.SCJGCJ.COM?OnQuery(CefRefPtr<CefBrowser>browser,

CefRefPtr<CefFrame>frame, int64 query_id,

const CefString&request, bool persistent, CefRefPtr<Callback>callback)

{

if (request == "HelloCefQuery")

{

callback->Success("HelloCefQuery Ok")

return true

}

else if (request == "GiveMeMoney")

{

callback->Failure(404, "There are none thus query!")

return true

}

return false// give other handler chance

}

void BrowserSideJSHandler::OnQueryCanceled(CefRefPtr<CefBrowser>browser, CefRefPtr<CefFrame>frame, int64 query_id)

{

//cancel our async query task...

}

5)给CefMessageRouterBrowserSide的实例添加handler

我在ClientHandler类中定义了一个BrowserSideJSHandler的实例,类似下面:

class ClientHandler : public CefClient,

public CefDisplayHandler,

public CefLifeSpanHandler,

public CefLoadHandler,

public CefRequestHandler

{

...

CefRefPtr<CefMessageRouterBrowserSide>m_browser_side_router

BrowserSideJSHandler m_jsHandler

...

}

然后在ClientHandler构造函数中加入了下列代码:

m_browser_side_router->AddHandler(&m_jsHandler, true)

上面的代码把BrowserSideJSHandler加入到browser进程里的消息路由器中。

好了,到这里browser进程配置完成。接下来看renderer进程。