转载:http://blog.****.net/foruok/article/details/50573612
转载:http://blog.****.net/foruok/article/details/50584985
转载:http://blog.****.net/mfcing/article/details/44539035
转载:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF_JavaScript_Cpp.md
转载:https://blog.****.net/aseseven/article/details/79482515(CEF3加载本地HTML文件时中文路径乱码的问题解决办法)
转载:https://blog.****.net/u012778714/article/category/7003599
JS与Native代码交互,是在Render进程中,所以我们要实现CefRenderProcessHandler接口
一、JS 调用 C++
- JavaScript注册函数给Render进程,Render进程保存该JavaScript函数
- Render进程发消息通知Browser进程
- Browser进程处理后,回发消息给Render进程
- Render进程调用之前保存的JavaScript函数
1.带参数没有返回值
自己的APP类要继承于CefRenderProcessHandler
#ifndef _CEFBROWSERAPP_H_
#define _CEFBROWSERAPP_H_
#include "include/cef_app.h"
#include "CEFV8HandlerEx.h" class CCefBrowserApp
: public CefApp
, public CefBrowserProcessHandler
, public CefRenderProcessHandler
{
public:
CCefBrowserApp(); virtual ~CCefBrowserApp(); public:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()OVERRIDE { return this; }; public:
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized(); //CefRenderProcessHandler methods
virtual void OnWebKitInitialized(); CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE{ return this; } virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context); virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context); protected: CefRefPtr<CCEFV8HandlerEx> m_v8Handler; IMPLEMENT_REFCOUNTING(CCefBrowserApp);
};
#endif //_CEFBROWSERAPP_H_
.cpp
#include "CefBrowserApp.h"
#include "stdafx.h" CCefBrowserApp::CCefBrowserApp()
:m_v8Handler(new CCEFV8HandlerEx)
{
} CCefBrowserApp::~CCefBrowserApp()
{
} void CCefBrowserApp::OnContextInitialized()
{
// do nothing here, because we will create browser in my own dialog
} void CCefBrowserApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
// 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 CCefBrowserApp::OnWebKitInitialized()
{
std::string app_code =
"var app;"
"if (!app)"
" app = {};"
"(function() {"
" app.GetId = function() {"
" native function GetId();"
" return GetId();"
" };"
"})();"; // Registered Javascript Function, which will be called by Cpp
" app.registerJavascriptFunction = function(name,callback) {"
" native function registerJavascriptFunction();"
" return registerJavascriptFunction(name,callback);"
" };" "})();"; CefRegisterExtension("v8/app", app_code, m_v8Handler);//第一个参数不能为空,否则报错,这个名字可以自定义
} 注:CefRegisterExtension的注释
// Example JavaScript extension code:
// <pre>
// // create the 'example' global object if it doesn't already exist.
// if (!example)
// example = {};
// // create the 'example.test' global object if it doesn't already exist.
// if (!example.test)
// example.test = {};
// (function() {
// // Define the function 'example.test.myfunction'.
// example.test.myfunction = function() {
// // Call CefV8Handler::Execute() with the function name 'MyFunction'
// // and no arguments.
// native function MyFunction();
// return MyFunction();
// };
// // Define the getter function for parameter 'example.test.myparam'.
// example.test.__defineGetter__('myparam', function() {
// // Call CefV8Handler::Execute() with the function name 'GetMyParam'
// // and no arguments.
// native function GetMyParam();
// return GetMyParam();
// });
// // Define the setter function for parameter 'example.test.myparam'.
// example.test.__defineSetter__('myparam', function(b) {
// // Call CefV8Handler::Execute() with the function name 'SetMyParam'
// // and a single argument.
// native function SetMyParam();
// if(b) SetMyParam(b);
// });
//
// // Extension definitions can also contain normal JavaScript variables
// // and functions.
// var myint = 0;
// example.test.increment = function() {
// myint += 1;
// return myint;
// };
// })();
// </pre>
// Example usage in the page:
// <pre>
// // Call the function.
// example.test.myfunction();
// // Set the parameter.
// example.test.myparam = value;
// // Get the parameter.
// value = example.test.myparam;
// // Call another function.
// example.test.increment();
// </pre>
///
void CCefBrowserApp::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
m_v8Handler = nullptr;
}
OnContextCreated给window对象绑定了一个NativeLogin函数,这个函数将由ClientV8Handler类来处理,当HTML中的JS代码调用window.NativeLogin时,ClientV8Handler的Execute方法会被调用。
OnWebKitInitialized注册了一个名为app的JS扩展,在这个扩展里为app定义了GetId方法,app.GetId内部调用了native版本的GetId()。HTML中的JS代码可能如下:
alert(app.GetId());
当浏览器执行上面的代码时,CCEFV8HandlerEx的Execute方法会被调用,现在来看CCEFV8HandlerEx的实现
.h
#ifndef _CEFV8HANDLEREX_H_
#define _CEFV8HANDLEREX_H_ #include "include/cef_v8.h" class CCEFV8HandlerEx : public CefV8Handler {
public:
CCEFV8HandlerEx(); ~CCEFV8HandlerEx();
public:
virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) override;
private:
// Map of message callbacks.
typedef std::map<std::pair<std::string, int>, std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > >CallbackMap;
CallbackMap callback_map_; protected:
IMPLEMENT_REFCOUNTING(CCEFV8HandlerEx);
};
#endif//_CEFV8HANDLEREX_H_
.cpp
#include "CEFV8HandlerEx.h"
#include "stdafx.h"
#include <strsafe.h> CCEFV8HandlerEx::CCEFV8HandlerEx()
{ } CCEFV8HandlerEx::~CCEFV8HandlerEx()
{
// Remove any JavaScript callbacks registered for the context that has been released.
if (!callback_map_.empty()) {
CallbackMap::iterator it = callback_map_.begin();
for (; it != callback_map_.end();) {
if (it->second.first->IsSame(it->second.first))
callback_map_.erase(it++);
else
++it;
}
}
} bool CCEFV8HandlerEx::Execute(const CefString& name /*JavaScript调用的C++方法名字*/, CefRefPtr<CefV8Value> object /*JavaScript调用者对象*/, const CefV8ValueList& arguments /*JavaScript传递的参数*/, CefRefPtr<CefV8Value>& retval /*返回给JS的值设置给这个对象*/, CefString& exception/*通知异常信息给JavaScript*/)
{
if (name == "NativeLogin")
{//Window Binding
if (arguments.size() == )
{
CefString strUser = arguments.at()->GetStringValue();
CefString strPassword = arguments.at()->GetStringValue(); //TODO: doSomething() in native way CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("login_msg"); // Retrieve the argument list object.
CefRefPtr<CefListValue> args = msg->GetArgumentList(); // Populate the argument values.
args->SetSize();
args->SetString(, strUser);
args->SetString(, strPassword); // Send the process message to the browser process.
CefV8Context::GetCurrentContext()->GetBrowser()->SendProcessMessage(PID_BROWSER, msg); retval = CefV8Value::CreateInt();//函数的返回值 我们可以拿这个返回值做判断或者其他操作
//var result = window.NativeLogin(document.getElementById("userName").value, document.getElementById("password").value);
//document.getElementById("text").innerHTML = result
}
else
{
retval = CefV8Value::CreateInt();
}
return true;
}
else if (name == "GetId")
{//JS Extensions
if (arguments.size() == )
{
// execute javascript
// just for test
CefRefPtr<CefFrame> frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
frame->ExecuteJavaScript("alert('Hello, I came from native world.')", frame->GetURL(), ); // return to JS
retval = CefV8Value::CreateString("");
return true;
}
}
// Function does not exist.
return false;
}
在Browser进程中接受Render进程发过来的消息
重写 virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process,CefRefPtr<CefProcessMessage> message);这个虚函数
我把C++函数都写在了窗口类中,所以我对CCefBrowserEventHandler做了修改,把窗口指针传到Browser中,方便调用
.h
#ifndef _CEFBROWSEREVENTHANDLER_H_
#define _CEFBROWSEREVENTHANDLER_H_
#include "include/cef_client.h"
#include "include/base/cef_lock.h" //线程安全 class CMainFrameWnd; class CCefBrowserEventHandler
: public CefClient
, public CefDisplayHandler // 显示变化事件
, public CefLoadHandler // 加载错误事件
, public CefLifeSpanHandler // 声明周期事件
//, public CefContextMenuHandler // 上下文菜单事件
//, public CefDialogHandler // 对话框事件
//, public CefDownloadHandler // 下载事件
//, public CefDragHandler // 拖拽事件
//, public CefFindHandler // 查找事件
//, public ...
{
public:
CCefBrowserEventHandler(CMainFrameWnd* pMainFrame); virtual ~CCefBrowserEventHandler(); public:
// CefClient 事件处理器,如果没有对应处理器则默认使用内部处理器
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE;
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE;
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE; public:
// display handler method
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE; public:
// load handler method
virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) OVERRIDE; public:
// display handler meethod
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE; virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message); bool IsClosing() const { return m_bIsClosing; } CefRefPtr<CefBrowser> GetBrowser(){return m_Browser;} protected: CefRefPtr<CefBrowser> m_Browser; bool m_bIsClosing; CMainFrameWnd* m_pMainWnd; IMPLEMENT_REFCOUNTING(CCefBrowserEventHandler);
//由于CEF采用多线程架构,有必要使用锁和闭包来保证在多不同线程安全的传递数据。IMPLEMENT_LOCKING定义提供了Lock()和Unlock()方法以及AutoLock对象来保证不同代码块同步访问
IMPLEMENT_LOCKING(CCefBrowserEventHandler);//必须包含#include "include/base/cef_lock.h"
}; #endif//_CEFBROWSEREVENTHANDLER_H_
.cpp
#include "CefBrowserEventHandler.h"
#include "stdafx.h"
#include <sstream>
#include <string>
#include "include/cef_app.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "MainFrameWnd.h" CCefBrowserEventHandler::CCefBrowserEventHandler(CMainFrameWnd* pMainFrame)
:m_bIsClosing(false)
,m_pMainWnd(pMainFrame)
{ } CCefBrowserEventHandler::~CCefBrowserEventHandler()
{ } CefRefPtr<CefDisplayHandler> CCefBrowserEventHandler::GetDisplayHandler()
{
return this;
} CefRefPtr<CefLifeSpanHandler> CCefBrowserEventHandler::GetLifeSpanHandler()
{
return this;
} CefRefPtr<CefLoadHandler> CCefBrowserEventHandler::GetLoadHandler()
{
return this;
} void CCefBrowserEventHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title)
{ } void CCefBrowserEventHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode,
const CefString& errorText, const CefString& failedUrl)
{
CEF_REQUIRE_UI_THREAD();
if (ERR_ABORTED == errorCode)
return ; std::stringstream ss;
ss << "<html><body bgcolor=\"white\">"
"<h2>Failed to load URL " << std::string(failedUrl) <<
" with error " << std::string(errorText) << " (" << errorCode <<
").</h2></body></html>";
frame->LoadString(ss.str(), failedUrl);
} void CCefBrowserEventHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD(); //base::AutoLock lock_scope(lock_); AutoLock lock_scope(this); m_Browser = browser; } bool CCefBrowserEventHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD(); //base::AutoLock lock_scope(lock_);
AutoLock lock_scope(this); if(m_Browser)
{
// Set a flag to indicate that the window close should be allowed.
m_bIsClosing = true;
} return false;
} void CCefBrowserEventHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD(); //base::AutoLock lock_scope(lock_);
AutoLock lock_scope(this); if(m_Browser->IsSame(browser))
m_Browser = NULL;
} bool CCefBrowserEventHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "login_msg")
{
// extract message
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefString strUser = args->GetString();
CefString strPassword = args->GetString(); m_pMainWnd->CEFLoginJsCallCPP(strUser,strPassword);//窗口类的成员函数 //如果函数有返回值也可以通过向Render发送消息传递
//send reply to render process
CefRefPtr<CefProcessMessage> outMsg = CefProcessMessage::Create("login_reply"); // Retrieve the argument list object.
CefRefPtr<CefListValue> replyArgs = outMsg->GetArgumentList(); // Populate the argument values.
replyArgs->SetSize();
replyArgs->SetInt(, ); // Send the process message to the renderer process.
browser->SendProcessMessage(PID_RENDERER, outMsg); return true;
} return false;
}
Browser进程处理完后向Render进程发了消息,The render process receives the IPC message处理
.h
#ifndef _CEFBROWSERAPP_H_
#define _CEFBROWSERAPP_H_
#include "include/cef_app.h"
#include "CEFV8HandlerEx.h" class CCefBrowserApp
: public CefApp
, public CefBrowserProcessHandler
, public CefRenderProcessHandler
{
public:
CCefBrowserApp(); virtual ~CCefBrowserApp(); public:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()OVERRIDE { return this; }; public:
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized(); //CefRenderProcessHandler methods
virtual void OnWebKitInitialized(); CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE{ return this; } virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context); virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context); //收消息
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message); protected: CefRefPtr<CCEFV8HandlerEx> m_v8Handler; IMPLEMENT_REFCOUNTING(CCefBrowserApp);
};
#endif //_CEFBROWSERAPP_H_
.cpp
#include "CefBrowserApp.h"
#include "stdafx.h" CCefBrowserApp::CCefBrowserApp()
:m_v8Handler(new CCEFV8HandlerEx)
{
} CCefBrowserApp::~CCefBrowserApp()
{
} void CCefBrowserApp::OnContextInitialized()
{
// do nothing here, because we will create browser in my own dialog
} void CCefBrowserApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal(); // Create the "NativeLogin" function.
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("NativeLogin", m_v8Handler);//第一个参数和SetValue参数保持一致,否则无法调用 // Add the "NativeLogin" function to the "window" object.
object->SetValue("NativeLogin", func, V8_PROPERTY_ATTRIBUTE_NONE); // Add the "register" function to the "window" object.
object->SetValue("register",CefV8Value::CreateFunction("register", m_v8Handler),V8_PROPERTY_ATTRIBUTE_NONE);
} void CCefBrowserApp::OnWebKitInitialized()
{
std::string app_code =
"var app;"
"if (!app)"
" app = {};"
"(function() {"
" app.GetId = function() {"
" native function GetId();"
" return GetId();"
" };"
"})();"; // Registered Javascript Function, which will be called by Cpp
" app.registerJavascriptFunction = function(name,callback) {"
" native function registerJavascriptFunction();"
" return registerJavascriptFunction(name,callback);"
" };" "})();"; CefRegisterExtension("v8/app", app_code, m_v8Handler);//第一个参数不能为空
} void CCefBrowserApp::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
m_v8Handler = nullptr;
} bool CCefBrowserApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "login_reply")
{
// extract message
CefRefPtr<CefListValue> args = message->GetArgumentList();
bool status = args->GetBool(); CefRefPtr<CefFrame> frame = browser->GetMainFrame(); if (status)
{
frame->ExecuteJavaScript("IsSuccess();", frame->GetURL(), );
} return true;
} return false;
}
2.JS CallBack
在OnContextCreated()函数中给window绑定函数
// Create the "register" function.
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("register", m_v8Handler);//第一个参数和SetValue参数保持一致,否则无法调用 // Add the "register" function to the "window" object.
object->SetValue("register", func, V8_PROPERTY_ATTRIBUTE_NONE);
在Exectue()函数中处理
else if (name == "register")
{
if (arguments.size() == && arguments[]->IsFunction())
{
CefRefPtr<CefV8Value> callback_func_ = arguments[];
CefRefPtr<CefV8Context> callback_context_ = CefV8Context::GetCurrentContext(); callback_func_->ExecuteFunction(NULL, arguments);//执行回调函数 return true;
}
}
在HTML的JavaScript里这样写
function myFunc()
{
// do something in JS.
alert("callback");
} //js CALLback
function CallBack()
{
window.register(myFunc);
}
3.C++ 调用 JS
C++调用JS函数相对简单多了,因为CEF有接口可以直接使用CefFrame::ExecuteJavaScript,看看注释:
///
// 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) =;
首先需要获取到我们的浏览器里的主框架对象,code是JS函数和传入参数的字符串,URL可以直接忽略。
CefRefPtr<CefFrame> frame = m_handler->GetBrowser()->GetMainFrame(); m_handler是我们自己定义的Handler对象 /C++ 调用js方法
//frame->ExecuteJavaScript(L"Test();",frame->GetURL(),0);//提示框
//frame->ExecuteJavaScript(L"ModifyValue();",frame->GetURL(),0);//无参数函数
frame->ExecuteJavaScript(L"ModifyValue('巴萨牛逼');",frame->GetURL(),);//有参数函数
9 如果参数是可变的,可以这样
CString strJsCode;
strJsCode.Format(L"setInstallStatus('%s','%s','%d');", lpData->strId.c_str(), strStatus, nPercent);
其中setInstallStatus是js函数,它有三个参数
我在HMTL里写的
function Test()
{
alert("js被C++非礼了");
} function ModifyValue( arr)
{
//document.getElementById("text").innerHTML = "被修改了"; alert(arr);
document.getElementById("text").innerHTML = arr;
}