本文相关代码来源于 网易开源duilib https://github.com/netease-im/NIM_Duilib_Framework前端
0. 继承CefAPP CefRenderProcessHandlernode
class ClientApp : public CefApp, public CefRenderProcessHandler { public: ClientApp(); private: // CefApp methods. virtual void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) OVERRIDE; virtual void OnRegisterCustomSchemes( CefRefPtr<CefSchemeRegistrar> registrar) OVERRIDE;
// CefApp methods. void ClientApp::OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) { } ////////////////////////////////////////////////////////////////////////////////////////// void ClientApp::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar) { // Default schemes that support cookies. cookieable_schemes_.push_back("http"); cookieable_schemes_.push_back("https"); }
1 处理渲染处理器中的接口git
// CefRenderProcessHandler methods. virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE{ return this; } virtual void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) OVERRIDE; virtual void OnWebKitInitialized() OVERRIDE; virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE; virtual void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE; virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE; virtual bool OnBeforeNavigation( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, NavigationType navigation_type, bool is_redirect) OVERRIDE; virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE; virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE; virtual void OnUncaughtException( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception, CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE; virtual void OnFocusedNodeChanged( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> node) OVERRIDE; virtual bool OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) OVERRIDE;
// CefRenderProcessHandler methods. void ClientApp::OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) { } void ClientApp::OnWebKitInitialized() { /** * JavaScript 扩展代码,这里定义一个 NimCefWebFunction 对象提供 call 方法来让 Web 端触发 native 的 CefV8Handler 处理代码 * param[in] functionName 要调用的 C++ 方法名称 * param[in] params 调用该方法传递的参数,在前端指定的是一个 Object,但转到 Native 的时候转为了字符串 * param[in] callback 执行该方法后的回调函数 * 前端调用示例 * NimCefWebHelper.call('showMessage', { message: 'Hello C++' }, (arguments) => { * console.log(arguments) * }) */ std::string extensionCode = R"( var NimCefWebInstance = {}; (() => { NimCefWebInstance.call = (functionName, arg1, arg2) => { if (typeof arg1 === 'function') { native function call(functionName, arg1); return call(functionName, arg1); } else { const jsonString = JSON.stringify(arg1); native function call(functionName, jsonString, arg2); return call(functionName, jsonString, arg2); } }; NimCefWebInstance.register = (functionName, callback) => { native function register(functionName, callback); return register(functionName, callback); }; })(); )"; CefRefPtr<CefJSHandler> handler = new CefJSHandler(); if (!render_js_bridge_.get()) render_js_bridge_.reset(new CefJSBridge); handler->AttachJSBridge(render_js_bridge_); CefRegisterExtension("v8/extern", extensionCode, handler); } void ClientApp::OnBrowserCreated(CefRefPtr<CefBrowser> browser) { if (!render_js_bridge_.get()) render_js_bridge_.reset(new CefJSBridge); } void ClientApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) { } CefRefPtr<CefLoadHandler> ClientApp::GetLoadHandler() { return NULL; } bool ClientApp::OnBeforeNavigation( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, NavigationType navigation_type, bool is_redirect) { return false; } void ClientApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { } void ClientApp::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { render_js_bridge_->RemoveCallbackFuncWithFrame(frame); render_js_bridge_->UnRegisterJSFuncWithFrame(frame); } void ClientApp::OnUncaughtException( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception, CefRefPtr<CefV8StackTrace> stackTrace) { } void ClientApp::OnFocusedNodeChanged( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> node) { bool is_editable = (node.get() && node->IsEditable()); if (is_editable != last_node_is_editable_) { // Notify the browser of the change in focused element type. last_node_is_editable_ = is_editable; CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create(kFocusedNodeChangedMessage); message->GetArgumentList()->SetBool(0, is_editable); browser->SendProcessMessage(PID_BROWSER, message); } } bool ClientApp::OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) { ASSERT(source_process == PID_BROWSER); // 收到 browser 的消息回复 const CefString& message_name = message->GetName(); if (message_name == kExecuteJsCallbackMessage) { int callback_id = message->GetArgumentList()->GetInt(0); bool has_error = message->GetArgumentList()->GetBool(1); CefString json_string = message->GetArgumentList()->GetString(2); // 将收到的参数经过管理器传递给调用时传递的回调函数 render_js_bridge_->ExecuteJSCallbackFunc(callback_id, has_error, json_string); } else if (message_name == kCallJsFunctionMessage) { CefString function_name = message->GetArgumentList()->GetString(0); CefString json_string = message->GetArgumentList()->GetString(1); int cpp_callback_id = message->GetArgumentList()->GetInt(2); int64 frame_id = message->GetArgumentList()->GetInt(3); // 经过 C++ 执行一个已经注册过的 JS 方法 // frame_id 小于 0 则多是 browser 进程的 browser 是无效的,因此这里为了不出现错误就获取一个顶层 frame 执行代码 render_js_bridge_->ExecuteJSFunc(function_name, json_string, frame_id < 0 ? browser->GetMainFrame() : browser->GetFrame(frame_id), cpp_callback_id); } return false; }
2 继承CefV8Handler ,重载Execute方法,以便执行注册的V8/extern扩展的的JS方法,该方法调用C++方法github
class CefJSHandler : public CefV8Handler { public: CefJSHandler() {} virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE; void AttachJSBridge(std::shared_ptr<CefJSBridge> js_bridge) { js_bridge_ = js_bridge; } IMPLEMENT_REFCOUNTING(CefJSHandler); private: std::shared_ptr<CefJSBridge> js_bridge_; };
bool CefJSHandler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) { // 当Web中调用了"NimCefWebFunction"函数后,会触发到这里,而后把参数保存,转发到Broswer进程 // Broswer进程的BrowserHandler类在OnProcessMessageReceived接口中处理kJsCallbackMessage消息,就能够收到这个消息 if (arguments.size() < 2) { exception = "Invalid arguments."; return false; } CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); CefRefPtr<CefFrame> frame = context->GetFrame(); CefRefPtr<CefBrowser> browser = context->GetBrowser(); int64_t browser_id = browser->GetIdentifier(); int64_t frame_id = frame->GetIdentifier(); if (name == "call") { // 容许没有参数列表的调用,第二个参数为回调 // 若是传递了参数列表,那么回调是第三个参数 CefString function_name = arguments[0]->GetStringValue(); CefString params = "{}"; CefRefPtr<CefV8Value> callback; if (arguments[0]->IsString() && arguments[1]->IsFunction()) { callback = arguments[1]; } else if (arguments[0]->IsString() && arguments[1]->IsString() && arguments[2]->IsFunction()) { params = arguments[1]->GetStringValue(); callback = arguments[2]; } else { exception = "Invalid arguments."; return false; } // 执行 C++ 方法 if (!js_bridge_->CallCppFunction(function_name, params, callback)) { exception = nbase::StringPrintf("Failed to call function %s.", function_name).c_str(); return false; } return true; } else if (name == "register") { if (arguments[0]->IsString() && arguments[1]->IsFunction()) { std::string function_name = arguments[0]->GetStringValue(); CefRefPtr<CefV8Value> callback = arguments[1]; if (!js_bridge_->RegisterJSFunc(function_name, callback)) { exception = "Failed to register function."; return false; } return true; } else { exception = "Invalid arguments."; return false; } } return false; }