cef js返回c++的代码_CEF中JavaScript与C++交互

本文详细介绍了CEF框架下JavaScript与C++交互的两种方法:通过Window bindings和js扩展。首先分析了Browser与Render进程的区别,然后通过代码示例展示了如何在Renderer进程中通过CefRenderProcessHandler的OnContextCreated()方法绑定JS函数到window对象,以及通过OnWebKitInitialized()方法注册JS扩展。此外,还提供了一个名为cef_js_integration的演示项目,包含了多种JS交互方式的实现。
摘要由CSDN通过智能技术生成

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

在贴代码之前。先来看看Browser进程和Render进程是怎么回事儿。有什么不同。

Browser与Render进程

从cefsimple開始吧,cefsimple_win.cc中的wWinMain函数中调用了CefExecuteProcess()方法来检測是否要启动其他的子进程。此处的CefExecuteProcess是在libcef_dll_wrapper.cc中的,它内部又调用了cef_execute_process方法(libcef_dll.cc),cef_execute_process又调用了libcef/browser/context.cc文件内实现的CefExecuteProcess方法。这种方法代码例如以下:

int CefExecuteProcess(const CefMainArgs& args,

CefRefPtr application,

void* windows_sandbox_info) {

base::CommandLine command_line(base::CommandLine::NO_PROGRAM);

#if defined(OS_WIN)

command_line.ParseFromString(::GetCommandLineW());

#else

command_line.InitFromArgv(args.argc, args.argv);

#endif

// Wait for the debugger as early in process initialization as possible.

if (command_line.HasSwitch(switches::kWaitForDebugger))

base::debug::WaitForDebugger(60, true);

// If no process type is specified then it represents the browser process and

// we do nothing.

std::string process_type =

command_line.GetSwitchValueASCII(switches::kProcessType);

if (process_type.empty())

return -1;

CefMainDelegate main_delegate(application);

// Execute the secondary process.

#if defined(OS_WIN)

sandbox::SandboxInterfaceInfo sandbox_info = {0};

if (windows_sandbox_info == NULL) {

content::InitializeSandboxInfo(&sandbox_info);

windows_sandbox_info = &sandbox_info;

}

content::ContentMainParams params(&main_delegate);

params.instance = args.instance;

params.sandbox_info =

static_cast<:sandboxinterfaceinfo>(windows_sandbox_info);

return content::ContentMain(params);

#else

content::ContentMainParams params(&main_delegate);

params.argc = args.argc;

params.argv = const_cast(args.argv);

return content::ContentMain(params);

#endif

它分析了命令行參数,提取”type”參数。假设为空。说明是Browser进程,返回-1。这样一路回溯到wWinMain方法里,然后開始创建Browser进程相关的内容。

假设”type”參数不为空,做一些推断,最后调用了content::ContentMain方法,直到这种方法结束。子进程随之结束。

content::ContentMain方法再追溯下去,就到了chromium的代码里了。在chromium/src/content/app/content_main.cc文件里。

详细我们不分析了,感兴趣的能够去看看。

分析了CefExecuteProcess方法我们知道,Browser进程在cefsimple_win.cc内调用了CefExecuteProcess之后做了进一步的配置,这个是在simple_app.cc内完毕的,详细就是SimpleApp::OnContextInitialized()这种方法。代码例如以下:

void SimpleApp::OnContextInitialized() {

CEF_REQUIRE_UI_THREAD();

CefWindowInfo window_info;

window_info.SetAsPopup(NULL, "cefsimple");

// SimpleHandler implements browser-level callbacks.

CefRefPtr handler(new SimpleHandler());

// Specify CEF browser settings here.

CefBrowserSettings browser_settings;

std::string url;

CefRefPtr command_line =

CefCommandLine::GetGlobalCommandLine();

url = command_line->GetSwitchValue("url");

if (url.empty())

url = "http://www.google.com";

CefBrowserHost::CreateBrowser(window_info, handler.get(), url,

browser_settings, NULL);

}

能够看到。这里创建SimpleHandler,并传递给CefBrowserHost::CreateBrowser去使用。

如今我们清楚了,Browser进程,须要CefApp(SimpleApp实现了这个接口)和CefClient(SimpleHandler实现了这个接口)。而Renderer进程仅仅要CefApp。

另外,CEF还定义了CefBrowserProcessHandler和CefRenderProcessHandler两个接口,分别来处理Browser进程和Render进程的个性化的通知。

因此,Browser进程的App一般还须要实现CefBrowserProcessHandler接口,Renderer进程的App一般还须要实现CefRenderProcessHandler接口。这里https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage有详细说明。

像cefsimple这个演示样例中的SimpeApp,没有实现CefRenderProcessHandler接口,没有针对Renderer进程做特别处理,所以当它作为Render进程时。会缺失一部分功能。

比方JS与Native代码交互,这正是我们想要的。

假设要实现JS与Native代码交互,最好分开实现Browser进程须要的CefApp和Render进程须要的CefApp。

像以下这样:

class ClientAppRenderer : public CefApp,

public CefRenderProcessHandler

{

...

}

class ClientAppBrowser : public CefApp,

public CefBrowserProcessHandler

{

...

}

当我们实现了CefRenderProcessHandler接口,就能够在其OnContextCreated()方法中获取到CefFrame相应的window对象。在它上面绑定一些JS函数或对象。然后JS代码里就能够通过window对象訪问。假设是函数。就会调用到我们实现的CefV8Handler接口的Execute方法。

第二种实现JS与Native交互的方式,是在实现CefRenderProcessHandler的OnWebKitInitialized()方法时导出JS扩展,详细參考https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md,有详细说明。

cef_js_integration项目

cef_js_integration是非常easy的演示样例。用来演示JS与Native的交互。它在一个项目内实现了ClientAppBrowser、ClientAppRenderer、ClientAppOther三种CefApp。分别相应Browser、Render及其他类别的三种进程。

JS和Native代码的交互发生在Render进程。App须要继承CefRenderProcessHandler来整合JS相关功能。因此在应用在启动时做了进程类型推断。依据不同的进程类型创建不同的CefApp。

这个演示样例演示了三种JS交互方式(參见https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md):

- 在native代码中通过CefFrame::ExecuteJavaScript()来运行JavaScript代码

- 将函数或对象绑定到CefFrame相应的window对象上。JS代码通过window对象訪问native代码导出的函数或对象

- 使用CefRegisterExtension()注冊JS扩展,JS直接訪问注冊到JS Context中的对象

这个项目參考了cefsimple、cefclient,还有https://github.com/acristoffers/CEF3SimpleSample,终于它比cefsimple复杂一点,比cefclient简单非常多。

好啦,背景几乎相同,上源代码。

cef_js_integration.cpp:

#include

#include

#include "cef_js_integration.h"

#include

#include

#include "include/cef_app.h"

#include "include/cef_browser.h"

#include "ClientAppBrowser.h"

#include "ClientAppRenderer.h"

#include "ClientAppOther.h"

#include "include/cef_command_line.h"

#include "include/cef_sandbox_win.h"

//#define CEF_USE_SANDBOX 1

#if defined(CEF_USE_SANDBOX)

#pragma comment(lib, "cef_sandbox.lib")

#endif

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,

_In_opt_ HINSTANCE hPrevInstance,

_In_ LPTSTR lpCmdLine,

_In_ int nCmdShow)

{

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

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

CefEnableHighDPISupport();

CefMainArgs main_args(hInstance);

void* sandbox_info = NULL;

#if defined(CEF_USE_SANDBOX)

CefScopedSandboxInfo scoped_sandbox;

sandbox_info = scoped_sandbox.sandbox_info();

#endif

// Parse command-line arguments.

CefRefPtr command_line = CefCommandLine::CreateCommandLine();

command_line->InitFromString(::GetCommandLineW());

// Create a ClientApp of the correct type.

CefRefPtr app;

// The command-line flag won't be specified for the browser process.

if (!command_line->HasSwitch("type"))

{

app = new ClientAppBrowser();

}

else

{

const std::string& processType = command_line->GetSwitchValue("type");

if (processType == "renderer")

{

app = new ClientAppRenderer();

}

else

{

app = new ClientAppOther();

}

}

// Execute the secondary process, if any.

int exit_code = CefExecuteProcess(main_args, app, sandbox_info);

if (exit_code >= 0)

return exit_code;

// Specify CEF global settings here.

CefSettings settings;

#if !defined(CEF_USE_SANDBOX)

settings.no_sandbox = true;

#endif

// Initialize CEF.

CefInitialize(main_args, settings, app.get(), sandbox_info);

// Run the CEF message loop. This will block until CefQuitMessageLoop() is

// called.

CefRunMessageLoop();

// Shut down CEF.

CefShutdown();

return 0;

}

能够看到。_tWinMain方法中解析了命令行參数,依据进程类型创建了不同的CefApp。

这是它与cefsimple的差别。

ClientAppBrowser类与cefsimple演示样例中的SimpleApp基本一致,略过。

ClientAppRender类在ClientAppRender.h和ClientAppRender.cpp中实现。先是ClientAppRender.h:

#ifndef CEF3_CLIENT_APP_RENDERER_H

#define CEF3_CLIENT_APP_RENDERER_H

#include "include/cef_app.h"

#include "include/cef_client.h"

#include "V8handler.h"

class ClientAppRenderer : public CefApp,

public CefRenderProcessHandler

{

public:

ClientAppRenderer();

CefRefPtr GetRenderProcessHandler() OVERRIDE

{

return this;

}

void OnContextCreated(

CefRefPtr browser,

CefRefPtr frame,

CefRefPtr context);

void OnWebKitInitialized() OVERRIDE;

private:

CefRefPtr m_v8Handler;

IMPLEMENT_REFCOUNTING(ClientAppRenderer);

};

#endif

ClientAppRender聚合了ClientV8Handler类的实例,回头再说。先来看ClientAppRender的OnContextCreated和OnWebKitInitialized,它们是实现JS与Native交互的关键。代码例如以下:

#include "ClientAppRenderer.h"

#include "V8handler.h"

#include

#include

ClientAppRenderer::ClientAppRenderer()

: m_v8Handler(new ClientV8Handler)

{

}

void ClientAppRenderer::OnContextCreated(CefRefPtr browser,

CefRefPtr frame,

CefRefPtr context)

{

OutputDebugString(_T("ClientAppRenderer::OnContextCreated, create window binding\r\n"));

// Retrieve the context's window object.

CefRefPtr object = context->GetGlobal();

// Create the "NativeLogin" function.

CefRefPtr func = CefV8Value::CreateFunction("NativeLogin", m_v8Handler);

// Add the "NativeLogin" function to the "window" object.

object->SetValue("NativeLogin", func, V8_PROPERTY_ATTRIBUTE_NONE);

}

void ClientAppRenderer::OnWebKitInitialized()

{

OutputDebugString(_T("ClientAppRenderer::OnWebKitInitialized, create js extensions\r\n"));

std::string app_code =

"var app;"

"if (!app)"

" app = {};"

"(function() {"

" app.GetId = function() {"

" native function GetId();"

" return GetId();"

" };"

"})();";

CefRegisterExtension("v8/app", app_code, m_v8Handler);

}

OnContextCreated给window对象绑定了一个NativeLogin函数,这个函数将由ClientV8Handler类来处理,当HTML中的JS代码调用window.NativeLogin时,ClientV8Handler的Execute方法会被调用。

OnWebKitInitialized注冊了一个名为app的JS扩展,在这个扩展里为app定义了GetId方法,app.GetId内部调用了native版本号的GetId()。HTML中的JS代码可能例如以下:

alert(app.GetId());

当浏览器运行上面的代码时,ClientV8Handler的Execute方法会被调用。

好啦。如今来看ClientV8Handler的实现(V8Handler.cpp):

#include "V8handler.h"

#include

#include

bool ClientV8Handler::Execute(const CefString& name,

CefRefPtr object,

const CefV8ValueList& arguments,

CefRefPtr& retval,

CefString& exception)

{

if (name == "NativeLogin")

{

if (arguments.size() == 2)

{

CefString strUser = arguments.at(0)->GetStringValue();

CefString strPassword = arguments.at(1)->GetStringValue();

TCHAR szLog[256] = { 0 };

_stprintf_s(szLog, 256, _T("user - %s, password - %s\r\n"), strUser.c_str(), strPassword.c_str());

OutputDebugString(szLog);

//TODO: doSomething() in native way

retval = CefV8Value::CreateInt(0);

}

else

{

retval = CefV8Value::CreateInt(2);

}

return true;

}

else if (name == "GetId")

{

if (arguments.size() == 0)

{

// execute javascript

// just for test

CefRefPtr frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();

frame->ExecuteJavaScript("alert('Hello, I came from native world.')", frame->GetURL(), 0);

// return to JS

retval = CefV8Value::CreateString("72395678");

return true;

}

}

// Function does not exist.

return false;

}

Execute在处理GetId方法时,还使用CefFrame::ExecuteJavaScript演示了怎样在native代码中运行JS代码。

最后。来看一下html代码:

function Login(){

window.NativeLogin(document.getElementById("userName").value, document.getElementById("password").value);

}

function GetId(){

alert("get id from native by extensions: " + app.GetId());

}

CEF JS Integration

Call into native by Window bindings:

UserName:   Password:


Call into native by js extensions:

通过以下的命令能够測试:

cef_js_integration.exe --url=file:///cef_js_integration.html

好啦,JS与Native交互的演示样例就到这里了。

其他參考文章:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值