// 这个成员方法是public类型的
void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override;
// 这个成员变量是private类型的
CefRefPtr<V8Handler> v8Handler;
void Renderer::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {
CefRefPtr<CefV8Value> globalObject = context->GetGlobal();
v8Handler = new V8Handler();
CefRefPtr<CefV8Value> nativeCall = CefV8Value::CreateFunction("nativeCall", v8Handler);
globalObject->SetValue("nativeCall", nativeCall, V8_PROPERTY_ATTRIBUTE_READONLY);
}
#pragma once
#include "include/cef_v8.h"
class V8Handler : public CefV8Handler
{
public:
V8Handler() = default;
virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) override;
private:
IMPLEMENT_REFCOUNTING(V8Handler);
};
#include "V8Handler.h"
bool V8Handler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
auto msgName = arguments[0]->GetStringValue();
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create(msgName);
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
context.get()->GetFrame()->SendProcessMessage(PID_BROWSER, msg);
return true;
};
> window.nativeCall
< ƒ nativeCall() { [native code] }
let browserWindow = {
getMsgName(args) {
return `window_${args.callee.name}`
},
minimize() {
let msgName = this.getMsgName(arguments);
window.nativeCall(msgName);
},
maximize() {
let msgName = this.getMsgName(arguments);
window.nativeCall(msgName);
},
close() {
let msgName = this.getMsgName(arguments);
window.nativeCall(msgName);
},
restore() {
let msgName = this.getMsgName(arguments);
window.nativeCall(msgName);
},
}
let minimizeBtn = document.querySelector("#minimizeBtn");
let maximizeBtn = document.querySelector("#maximizeBtn");
let restoreBtn = document.querySelector("#restoreBtn");
let closeBtn = document.querySelector("#closeBtn");
minimizeBtn.addEventListener("click", () => browserWindow.minimize());
closeBtn.addEventListener("click", () => browserWindow.close())
maximizeBtn.addEventListener("click", () => {
browserWindow.maximize();
maximizeBtn.setAttribute("style", "display:none");
restoreBtn.removeAttribute("style");
})
restoreBtn.addEventListener("click", () => {
browserWindow.restore();
restoreBtn.setAttribute("style", "display:none");
maximizeBtn.removeAttribute("style");
})
我们为窗口标题栏的按钮添加了点击事件的处理逻辑,而且在执行这些点击事件的时候,我们成功的通过 JavaScript 代码调用了在渲染进程中实现的 C++ 代码。但这还远远不够,用户点击这些标题栏按钮的时候,窗口状态仍旧没有变化,这是因为我们虽然把控制消息发送给了浏览器进程,但还没有为浏览器进程撰写接收消息和处理消息的逻辑。
主进程接收并处理渲染进程消息
bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) override;
bool PageHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
CEF_REQUIRE_UI_THREAD();
std::string messageName = message->GetName();
std::vector<std::string> arr = split(messageName, '_');
if (arr.at(0) == "window"){
CefRefPtr<CefBrowserView> browserView = CefBrowserView::GetForBrowser(browser);
CefRefPtr<CefWindow> window = browserView->GetWindow();
if (arr.at(1) == "minimize") {
window->Minimize();
}
else if (arr.at(1) == "maximize") {
window->Maximize();
}
else if (arr.at(1) == "close") {
window->Close();
}
else if (arr.at(1) == "restore") {
window->Restore();
}
}
return true;
}
std::vector<std::string> split(const std::string& s, char delim) {
std::vector<std::string> elems;
std::istringstream iss(s);
std::string item;
while (std::getline(iss, item, delim)) {
elems.push_back(item);
}
return elems;
}
let eventer = {
//事件容器
dic: {},
//监听事件
on(eventName, callBack) {
if (!this.dic[eventName]) this.dic[eventName] = [callBack]
else this.dic[eventName].push(callBack)
},
//发射事件
emit(eventName,...obj) {
if (!this.dic[eventName]) {
console.warn(`没有找到该事件的监听函数:${eventName}`)
return;
}
this.dic[eventName].forEach((func) => func(...obj))
},
//取消监听事件
off(eventName, callBack) {
if (!this.dic[eventName]) return
if (!callBack) {
delete this.dic[eventName]
return
}
let index = this.dic[eventName].findIndex((v) => v == callBack)
if (index >= 0) this.dic[eventName].splice(index, 1)
if (this.dic[eventName].length < 1) delete this.dic[eventName];
}
//监听一次性事件
once(eventName, callBack) {
let callBackWrap = (...obj) => {
let index = this.dic[eventName].findIndex((v) => v == callBackWrap)
if (index >= 0) this.dic[eventName].splice(index, 1)
if (this.dic[eventName].length < 1) delete this.dic[eventName];
callBack(...obj)
}
if (!this.dic[eventName]) this.dic[eventName] = [callBackWrap]
else this.dic[eventName].push(callBackWrap)
},
}
eventer.on("window_maximize", () => {
maximizeBtn.setAttribute("style", "display:none");
restoreBtn.removeAttribute("style");
})
eventer.on("window_unMaximize", () => {
restoreBtn.setAttribute("style", "display:none");
maximizeBtn.removeAttribute("style");
})
maximized: false,
isMaximized() {
let hSpan = window.outerHeight - screen.availHeight
let wSpan = window.outerWidth - screen.availWidth
return Math.abs(hSpan) < 2 && Math.abs(wSpan) < 2
},
init() {
window.addEventListener('resize', () => {
let curState = this.isMaximized()
let oldState = this.maximized
this.maximized = curState
if (oldState && !curState) eventer.emit(`window_unMaximize`)
else if (!oldState && curState) eventer.emit(`window_maximize`)
})
}
let native = {
randomNum(len = 12) {
return Math.floor(Math.pow(10, len) * Math.random());
},
call(msgName, ...params) {
return new Promise((resolve, reject) => {
let eventName = `${msgName}_${this.randomNum()}`;
eventer.once(eventName, (obj) => {
resolve(obj);
});
window.nativeCall(eventName, ...params);
});
},
init() {
window.nativeCall(`native_registe_callback`, (msgName, ...otherParams) => {
eventer.emit(msgName, ...otherParams);
});
},
};
native.init();
let system = {
getMsgName(args) {
return `system_${args.callee.name}`;
},
async getOSVersion() {
let msgName = this.getMsgName(arguments);
let osVersion = await native.call(msgName);
return osVersion;
},
};
bool V8Handler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
auto msgName = arguments[0]->GetStringValue();
if (msgName == "native_registe_callback") {
callBack = arguments[1];
return true;
}
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create(msgName);
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
context.get()->GetFrame()->SendProcessMessage(PID_BROWSER, msg);
return true;
};
public:
CefRefPtr<CefV8Value> callBack;
v8Handler->callBack->ExecuteFunction(nullptr, obj);
else if(arr.at(0) == "system") {
// messageName是在进入OnProcessMessageReceived方法时就通过如下代码获得了,它就是渲染进程发来的消息名
// std::string messageName = message->GetName();
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create(messageName);
CefRefPtr<CefListValue> msgArgs = msg->GetArgumentList();
if (arr.at(1) == "getOSVersion") {
std::string version = getOSVersion();
msgArgs->SetString(0, version);
}
frame->SendProcessMessage(PID_RENDERER, msg);
}
const std::string getOSVersion()
{
NTSTATUS(WINAPI* RtlGetVersion)(LPOSVERSIONINFOEXW);
OSVERSIONINFOEXW osInfo;
std::string result;
*(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandle(L"ntdll"), "RtlGetVersion");
if (nullptr != RtlGetVersion) {
osInfo.dwOSVersionInfoSize = sizeof osInfo;
RtlGetVersion(&osInfo);
result = std::to_string(osInfo.dwMajorVersion) +"." + std::to_string(osInfo.dwMinorVersion) +"." + std::to_string(osInfo.dwBuildNumber);
}
return result;
}
bool Renderer::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
CefString messageName = message->GetName();
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefString result = args->GetString(0);
CefRefPtr<CefV8Value> messageNameV8 = CefV8Value::CreateString(messageName);
CefRefPtr<CefV8Value> resultV8 = CefV8Value::CreateString(result);
CefV8ValueList argsForJs;
argsForJs.push_back(messageNameV8);
argsForJs.push_back(resultV8);
CefRefPtr<CefV8Context> context = frame->GetV8Context();
context->Enter();
v8Handler->callBack->ExecuteFunction(nullptr, argsForJs);
context->Exit();
return true;
}