CEF 渲染进程

本文相关代码来源于 网易开源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;
}