程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> libCEF中C++與JavaScript的交互調用

libCEF中C++與JavaScript的交互調用

編輯:C++入門知識

libCEF中C++與JavaScript的交互調用


前言

前一篇文章介紹過CEF在WIN32程序中嵌入chrome內核浏覽器的例子:http://blog.csdn.net/mfcing/article/details/43973377

這裡介紹的是嵌入浏覽器後,網頁的JS腳本函數與C++代碼的交互,這個很多地方都用得到。比如:音樂播放器裡網頁上的播放,客戶端資源中心裡的資源下載……

 

JS調用C++函數

首先需要重寫CefRenderProcessHandler的OnContextCreated接口,為什麼呢?學習CEF庫的使用必須仔細閱讀他的頭文件裡的注視部分:

 

  // Called immediately after the V8 context for a frame has been created. To
  // retrieve the JavaScript 'window' object use the CefV8Context::GetGlobal()
  // method. V8 handles can only be accessed from the thread on which they are
  // created. A task runner for posting tasks on the associated thread can be
  // retrieved via the CefV8Context::GetTaskRunner() method.
  ///
  /*--cef()--*/
  virtual void OnContextCreated(CefRefPtr browser,
                                CefRefPtr frame,
                                CefRefPtr context) {}
這個接口實在chrome的V8引擎創建後調用的,在這裡我們需要將JS裡面調用的函數和C++的執行函數關聯起來,這樣JS就可以“執行”C++代碼了。

 

我們的函數都是定義在window對象中的(不知道JS中這個是不是叫對象),根據注視我們需要GetGlobal獲取這個對象。

我的代碼是這樣的:

 

CefRefPtr window = context->GetGlobal();
		CefRefPtr myV8Acc = new CCefV8Accessor;
		CefRefPtr val = CefV8Value::CreateString(L"Application");
		CefString cefException;
		myV8Acc->Set(L"name", window, val, cefException);
		CefRefPtr pObjApp = CefV8Value::CreateObject(myV8Acc);
		window->SetValue(L"Application", pObjApp, V8_PROPERTY_ATTRIBUTE_NONE);

		CefRefPtr myV8handle = new CCefV8Handler();
		CefRefPtr myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle);
		static_cast(myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState);
		pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE);

		myFun = CefV8Value::CreateFunction(L"OneClickInstall", myV8handle);
		static_cast(myV8handle.get())->AddFun(L"OneClickInstall", &CChromeJsCallback::JsOneKeyInstall);
		pObjApp->SetValue(L"OneClickInstall", myFun, V8_PROPERTY_ATTRIBUTE_NONE);

		myFun = CefV8Value::CreateFunction(L"DownLoadFile", myV8handle);
		static_cast(myV8handle.get())->AddFun(L"DownLoadFile", &CChromeJsCallback::JsDownloadFile);
		pObjApp->SetValue(L"DownLoadFile", myFun, V8_PROPERTY_ATTRIBUTE_NONE);

所有的JS函數都是在window.Application上的,因此需要在window 對象上面創建Application 對象,CefV8Value::CreateObject用來創建對象。CefV8Value這個類在JS處理中至關重要,要必要好好看看頭文件裡面的注視,CEF的注視是相當詳細的。

 

 

創建函數對象,並將JS函數綁定到C++函數指針上面的過程是重點詳細介紹下

 

CefRefPtr myV8handle = new CCefV8Handler();
		CefRefPtr myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle);
		static_cast(myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState);
		pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE);

創建一個V8對象指針,但是new 的是一個CCefV8Handler,這個是需要自己擴展下的。我是這樣做的:

 

 

//定義JS回調函數指針
typedef bool(* JS_CALLBACK_FUN)(const CefV8ValueList& , CefRefPtr& );
typedef map FunctionMap;

class CCefV8Handler : 
	public CefV8Handler
{
public:
	CCefV8Handler();
	virtual bool Execute(const CefString& name, CefRefPtr object, \
		const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception);
	bool AddFun(const CefString& strName, JS_CALLBACK_FUN pFun);
private:
	FunctionMap	m_fun;
	// Include the default reference counting implementation.
	IMPLEMENT_REFCOUNTING(CCefV8Handler);
};

 

 

CCefV8Handler::CCefV8Handler()
{

}

bool CCefV8Handler::Execute( const CefString& name, CefRefPtr object, \
							const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception )
{
	FunctionMap::iterator itor = m_fun.find(name);
	if ( itor == m_fun.end() )
		return false;
	itor->second(arguments, retval);
	return true;
}

bool CCefV8Handler::AddFun( const CefString& strName, JS_CALLBACK_FUN pFun )
{
	if ( strName.empty() )
		return false;
	m_fun.insert(std::pair(strName, pFun));
	return true;
}


 


AddFun是自己添加的一個函數,用來把一個函數和氣對應的回調地址存儲到這個V8對象中,因此用了一個成員變量typedef map FunctionMap,調用函數時V8引擎有一個接口Execute,在這裡我們調用函數的名稱到map中去查找,找到了其對應的回調地址調用函數,這就是JS調用C++的過程了。

 

最上面那段代碼中,我們又添加了三個函數 SetAppState、OneClickInstall、DownLoadFile到window.application對象上面。

 

C++代碼中,這三個函數是這樣寫的:

 

//JS函數,在其他進程中調用
	static bool	JsSetAppState(const CefV8ValueList& argList, CefRefPtr& retValue);
	static bool	JsOneKeyInstall(const CefV8ValueList& argList, CefRefPtr& retValue);
	static bool	JsDownloadFile(const CefV8ValueList& argList, CefRefPtr& retValue);

vCefV8ValueList是一個參數列表,看它的定義typedef std::vector > CefV8ValueList,retValue當然就是函數的返回值了,有的JS需要根據返回值做相應的處理的。

 

這裡要注意:CEF可以使用單進程和多進程模式,我程序裡使用的是多進程模式,因此V8引擎的執行是在渲染引擎裡的,不要嘗試在這裡直接去對界面進行處理。界面進程和渲染進程是分開的,數據的話用共享內存來做,界面更新發消息來做。

 

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) =0;

首先需要獲取到我們的浏覽器裡的主框架對象,code是JS函數和傳入參數的字符串,URL可以直接忽略。

 

 

void CChromeBrowserUI::ExecuteJavascript( const wstring& strCode )
{
	if ( m_pWebBrowser.get() )
	{
		CefRefPtr frame = m_pWebBrowser->GetMainFrame();
		if ( frame.get() )
		{
			CefString strCode(strCode.c_str()), strUrl(L"");
			frame->ExecuteJavaScript(strCode, strUrl, 0);
		}
	}
}
注意:函數名和參數名需要用單引號分隔。

 

我的字符串格式化部分:

 

CString strJsCode;
	strJsCode.Format(L"setInstallStatus('%s','%s','%d');", lpData->strId.c_str(), strStatus, nPercent);

這樣,C++就可以調用JS的函數並傳入對應的參數了。

 

總結

要想使用CEF,最好還是仔細閱讀頭文件裡面的注釋,很詳細。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved