Interaction between C ++ and JavaScript in libCEF
Preface
The previous article introduced the example of how CEF embedded chrome kernel browser in WIN32 program: http://blog.csdn.net/mfcing/article/details/43973377
This section describes the interaction between JS script functions on the web page and C ++ code after the browser is embedded. For example, playing on a webpage in a music player, downloading resources in the client Resource Center ......
JS calls C ++ Functions
First, we need to rewrite the OnContextCreated interface of CefRenderProcessHandler. Why? To learn how to use the CEF library, you must carefully read the watch section in his header file:
// 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) {}
This interface is actually called After chrome V8 engine is created. here we need to associate the functions called in JS with the execution functions of C ++, in this way, JS can "execute" C ++ code.
All our functions are defined in the window object (I don't know if this is called an object in JS). GetGlobal needs to get this object according to the gaze.
My code is as follows:
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);
All JS functions are on window. Application. Therefore, you must create an Application object on the window object. CefV8Value: CreateObject is used to create an object. The CefV8Value class is very important in JS processing. It is necessary to take a good look at the gaze in the header file. the gaze of CEF is quite detailed.
The process of creating function objects and binding JS functions to C ++ function pointers is described in detail.
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);
Create a V8 Object Pointer, but the new one is a CCefV8Handler, which needs to be extended by yourself. I did this:
// Define the JS callback function pointer 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: FunctionMapm_fun; // Include the default reference counting implementation. Handler (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 is a self-added function that stores a function and the corresponding callback address in this V8 object. Therefore, a member variable typedef map is used. FunctionMap: when calling a function, the V8 engine has an interface Execute. Here we call the function name to find it in map and find its corresponding callback address to call the function, this is the process of JS Calling C ++.
In the code above, we added three functions SetAppState, OneClickInstall, and DownLoadFile to the window. application object.
In C ++ Code, these three functions are written as follows:
// JS function, which calls static boolJsSetAppState (const CefV8ValueList & argList, CefRefPtr
& RetValue); static boolJsOneKeyInstall (const CefV8ValueList & argList, CefRefPtr
& RetValue); static boolJsDownloadFile (const CefV8ValueList & argList, CefRefPtr
& RetValue );
VCefV8ValueList is a list of parameters. Its definition is typedef std: vector. > CefV8ValueList, retValue is of course the return value of the function, and some JS needs to handle it according to the return value.
Note: The CEF can use the single-process and multi-process modes. In my program, the multi-process mode is used. Therefore, the execution of the V8 engine is in the rendering engine, do not try to process the interface directly here. The interface process and the rendering process are separated. If the data is used, the shared memory is used, and the interface updates and sends messages.
C ++ calls JS Functions
C ++ is much easier to call JS functions, because CEF has interfaces that can directly use CefFrame: ExecuteJavaScript. Let's take a look at the notes:
// 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;
First, we need to obtain the main framework object in our browser. The code is a JS function and a string of input parameters. The URL can be directly ignored.
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);}}}
Note: function names and parameter names must be separated by single quotes.
My string formatting section:
CString strJsCode;strJsCode.Format(L"setInstallStatus('%s','%s','%d');", lpData->strId.c_str(), strStatus, nPercent);
In this way, C ++ can call JS functions and input corresponding parameters.
To sum up using CEF, it is best to carefully read the notes in the header file, which is very detailed.