CEF中JavaScript与C++交互
在CEF里,JS和Native(C/C++)代码能够非常方便的交互,这里https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md解说得非常清楚。我照着它实现了一个简单的交互演示样例。
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<CefApp> 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<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
return content::ContentMain(params);
#else
content::ContentMainParams params(&main_delegate);
params.argc = args.argc;
params.argv = const_cast<const char**>(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<SimpleHandler> handler(new SimpleHandler());
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
std::string url;
CefRefPtr<CefCommandLine> 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 <windows.h>
#include <tchar.h>
#include "cef_js_integration.h"
#include <string>
#include <algorithm>
#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<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
// Create a ClientApp of the correct type.
CefRefPtr<CefApp> 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<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE
{
return this;
}
void OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context);
void OnWebKitInitialized() OVERRIDE;
private:
CefRefPtr<ClientV8Handler> m_v8Handler;
IMPLEMENT_REFCOUNTING(ClientAppRenderer);
};
#endif
ClientAppRender聚合了ClientV8Handler类的实例,回头再说。先来看ClientAppRender的OnContextCreated和OnWebKitInitialized,它们是实现JS与Native交互的关键。代码例如以下:
#include "ClientAppRenderer.h"
#include "V8handler.h"
#include <Windows.h>
#include <tchar.h>
ClientAppRenderer::ClientAppRenderer()
: m_v8Handler(new ClientV8Handler)
{
}
void ClientAppRenderer::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
OutputDebugString(_T("ClientAppRenderer::OnContextCreated, create window binding\r\n"));
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create the "NativeLogin" function.
CefRefPtr<CefV8Value> 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 <Windows.h>
#include <tchar.h>
bool ClientV8Handler::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& 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<CefFrame> 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代码:
<!DOCTYPE html>
<html>
<!--
Copyright (c) 2016 foruok. All rights reserved.
欢迎关注foruok的微信订阅好“程序视界”。
-->
<head>
<script type="text/javascript">
function Login(){
window.NativeLogin(document.getElementById("userName").value, document.getElementById("password").value);
}
function GetId(){
alert("get id from native by extensions: " + app.GetId());
}
</script>
<title>CEF JS Integration</title>
</head>
<body>
<h3>Call into native by Window bindings:</h3>
<form>
UserName: <input type="text" id="userName" /> Password:
<input type="text" id="password" /> <input type="button" value="Login" onclick="Login()"/>
</form>
<hr>
<h3>Call into native by js extensions:</h3>
<input type="button" value="GetId" onclick="GetId()"/>
</html>
通过以下的命令能够測试:
cef_js_integration.exe --url=file:///cef_js_integration.html
好啦,JS与Native交互的演示样例就到这里了。
其他參考文章:
- CEF Windows开发环境搭建
- CEF载入PPAPI插件
- VS2013编译最简单的PPAPI插件
- 理解PPAPI的设计
- PPAPI插件与浏览器的交互过程
- Windows下从源代码编译CEF
- 编译PPAPI的media_stream_video演示样例
- PPAPI插件的画图与输入事件处理
- 在PPAPI插件中创建本地窗体
- PPAPI插件与浏览器的通信
- Windows下从源代码编译Skia
- 在PPAPI插件中使用Skia画图
- 载入DLL中的图片资源生成Skia中的SkBitmap对象
- PPAPI+Skia实现的涂鸦板
- PPAPI中使用Chromium的3D图形接口
- PPAPI中使用OpenGL ES画图
CEF中JavaScript与C++交互的更多相关文章
- iOS中JavaScript和OC交互
转载自:http://www.devzeng.com/blog/ios-uiwebview-interaction-with-javascript.html 还可参考的文章:http://blog.c ...
- iOS中JavaScript和OC交互 --by 胡 xu
在iOS开发中很多时候我们会和UIWebView打交道,目前国内的很多应用都采用了UIWebView的混合编程技术,最常见的是微信公众号的内容页面.前段时间在做微信公众平台相关的开发,发现很多应用场景 ...
- Duilib嵌入CEF以及JavaScript与C++交互
转载:http://blog.csdn.net/foruok/article/details/50573612 转载:http://blog.csdn.net/foruok/article/detai ...
- iOS UIWebView中javascript与Objective-C交互、获取摄像头
UIWebView是iOS开发中常用的一个视图控件,多数情况下,它被用来显示HTML格式的内容. 支持的文档格式 除了HTML以外,UIWebView还支持iWork, Office等文档格式: Ex ...
- UIWebView中javascript与Objective-C交互、获取摄像头
UIWebView是iOS开发中常用的一个视图控件,多数情况下,它被用来显示HTML格式的内容. 支持的文档格式 除了HTML以外,UIWebView还支持iWork, Office等文档格式: Ex ...
- CEF与JavaScript交互读取电脑信息
CefSharp中c#和JavaScript交互读取电脑信息 介绍 CEF是由Marshall Greenblatt于2008年创建的基于Google Chromium的BSD许可开源项目.与主要关注 ...
- asp.net中javascript与后台c#交互
asp.net中javascript与后台c#交互 作者:熊猫大叔 字体:[增加 减小] 类型:转载 时间:2015-10-23我要评论,出处:http://www.jb51.net/article/ ...
- 在android中实现webview与javascript之间的交互(转)
参见“在android中实现webview与javascript之间的交互”
- 使用C#在CEF中拦截并响应请求
一.前言 忙里偷闲,研究了一下如何在CEF中拦截请求,并作出响应.这个功能对某些需要修改服务器响应的需求来说必不可少,可以直接读取本地文件作为响应内容. C#的CEF封装项目有很多,我使用的是Chro ...
随机推荐
- 离线缓存 manifest
程序的离线缓存由一个叫做manifest的文本文件控制,把需要离线缓存的文件列在里面即可,这个列表还可以控制需要缓存的情况,甚至当用户从缓存地址进入到没有缓存的地址应该显示什么 当浏览器下载解析了ma ...
- python 3 ---购物车练习
# -*- coding:utf-8 -*-#Author Chen #定义商品列表List_of_commodities = [ ('Iphone',6888), ('Mac Pro',12888) ...
- python基础阶段 经典练习题 拾英札记(2)
因为编程的练习题是交互式的,在不断调试和不断渐进完善中,你会有一种成就感和快乐感,不断的修缮,不断的尝试. 其实,认知自己,和探索世界,也是这样的啊. 只要不放弃,要坚持. #7 根据列表lt,实现 ...
- [转载] Hibernate与 MyBatis的比较
转载自http://blog.csdn.net/firejuly/article/details/8190229 最近做了一个Hibernate与MyBatis的对比总结,希望大家指出不对之处. 第一 ...
- Python之子进程subprocess模块
http://www.cnblogs.com/vamei/archive/2012/09/23/2698014.html http://blog.csdn.net/jgood/article/deta ...
- 转载:Allegro实用技巧之模块复用
http://bbs.ednchina.com/BLOG_ARTICLE_3031842.HTM?click_from=8800032437,9950148743,2016-01-04,EDNCOL, ...
- 使用dropwizard(5)--加入swagger
前言 Swagger已经成API service的规范了,本处在dropwizard中简单集成Swagger. Demo source https://github.com/Ryan-Miao/l4d ...
- border-sizing属性
box-sizing属性可以为三个值之一:content-box(default),border-box,padding-box. content-box,border和padding不计算入widt ...
- 基于Vue.js的大型报告页项目实现过程及问题总结(二)
距离上一篇文章过去了二十多天了,期间一直想把第二部分写完,结果在测试过程中遇到了各种坑爹的问题,到今天才算基本完成,也许还有后续,但趁着今天有时间就写出来吧,也算对这个项目的一个总结了 遇到最大问题: ...
- 大雄和哆啦A梦
题目:大雄和哆啦A梦题目介绍:这个图片名称有点奇怪?! 1,打开链接会看到大雄和哆啦A梦的照片,把它下载下来.就是下面这个图片. 2,用wireshark打开,会看到最后面出现 rar ,还有flag ...