Interaction between JavaScript and C ++ in CEF

Source: Internet
Author: User

Interaction between JavaScript and C ++ in CEF

In CEF, JS and Native (C/C ++) code can easily interact with each other and be clearly explained. I followed it to implement a simple interaction example.

Before you paste the code, let's take a look at the differences between the Browser process and the Render process.

Browser and Render processes

Start with cefsimple. The wWinMain function in cefsimple_win.cc calls the CefExecuteProcess () method to check whether other sub-processes are to be started. Here, CefExecuteProcess is in executor. It calls the cef_execute_process method (libcef_dll.cc) internally, and cef_execute_process calls the CefExecuteProcess method implemented in the libcef/browser/context. cc file. The code for this method is as follows:

int CefExecuteProcess(const CefMainArgs& args,                      CefRefPtr
  
    application,                      void* windows_sandbox_info) {  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);#if defined(OS_WIN)  command_line.ParseFromString(::GetCommandLineW());#else  command_line.InitFromArgv(args.argc, args.argv);#endif  // Wait for the debugger as early in process initialization as possible.  if (command_line.HasSwitch(switches::kWaitForDebugger))    base::debug::WaitForDebugger(60, true);  // If no process type is specified then it represents the browser process and  // we do nothing.  std::string process_type =      command_line.GetSwitchValueASCII(switches::kProcessType);  if (process_type.empty())    return -1;  CefMainDelegate main_delegate(application);  // Execute the secondary process.#if defined(OS_WIN)  sandbox::SandboxInterfaceInfo sandbox_info = {0};  if (windows_sandbox_info == NULL) {    content::InitializeSandboxInfo(&sandbox_info);    windows_sandbox_info = &sandbox_info;  }  content::ContentMainParams params(&main_delegate);  params.instance = args.instance;  params.sandbox_info =      static_cast
   
    (windows_sandbox_info);  return content::ContentMain(params);#else  content::ContentMainParams params(&main_delegate);  params.argc = args.argc;  params.argv = const_cast
    
     (args.argv);  return content::ContentMain(params);#endif
    
   
  

It analyzes the command line parameters and extracts the "type" parameter. If it is null, it indicates that it is a Browser process and-1 is returned. This way, It is traced back to the wWinMain method, then, create content related to the Browser process.

If the "type" parameter is not empty, make some judgments and call the content: ContentMain method until the method ends, and the child process ends.

The content: ContentMain method goes back to the chromium code, in the chromium/src/content/app/content_main.cc file. We will not analyze the details. If you are interested, you can check them out.

After analyzing the CefExecuteProcess method, we know that the Browser process has made further configuration after the CefExecuteProcess is called in cefsimple_win.cc. This is completed in simple_app.cc, specifically SimpleApp: OnContextInitialized () the code for this method is as follows:

void SimpleApp::OnContextInitialized() {  CEF_REQUIRE_UI_THREAD();  CefWindowInfo window_info;  window_info.SetAsPopup(NULL, "cefsimple");  // SimpleHandler implements browser-level callbacks.  CefRefPtr
  
    handler(new SimpleHandler());  // Specify CEF browser settings here.  CefBrowserSettings browser_settings;  std::string url;  CefRefPtr
   
     command_line =      CefCommandLine::GetGlobalCommandLine();  url = command_line->GetSwitchValue("url");  if (url.empty())    url = "http://www.google.com";  CefBrowserHost::CreateBrowser(window_info, handler.get(), url,                                browser_settings, NULL);}
   
  

We can see that SimpleHandler is created here and passed to CefBrowserHost: CreateBrowser for use.

Now we know that the Browser process requires CefApp (SimpleApp implements this interface) and CefClient (SimpleHandler implements this interface ). The Renderer process only needs the CefApp.

In addition, CEF defines the CefBrowserProcessHandler and CefRenderProcessHandler interfaces to process personalized notifications of Browser and Render processes respectively. Therefore, the App of the Browser process generally needs to implement the CefBrowserProcessHandler interface, while the App of the Renderer process also needs to implement the CefRenderProcessHandler interface. Please refer to the following link for more information: https://bitbucket.org/chromiumembedded/cef/wiki/generalusage.

In the example of cefsimple, The SimpeApp does not implement the CefRenderProcessHandler interface and does not perform special processing on the Renderer process. Therefore, when it is used as a Render process, some functions are missing. For example, JS interacts with Native code, which is exactly what we want.

To implement interaction between JS and Native code, it is best to implement the CefApp required by the Browser process and the CefApp required by the Render process separately. As shown below:

class ClientAppRenderer : public CefApp,    public CefRenderProcessHandler {    ...}class ClientAppBrowser : public CefApp,     public CefBrowserProcessHandler{    ...}

After implementing the CefRenderProcessHandler interface, we can get the window object corresponding to CefFrame in its OnContextCreated () method and bind some JS functions or objects to it, then the JS Code can be accessed through the window object. If it is a function, the Execute method of the implemented CefV8Handler interface will be called.

Another method for implementing interaction between JS and Native is to export JS extensions when implementing the OnWebKitInitialized () method of CefRenderProcessHandler. For details, refer to examples.

Cef_js_integration Project

Cef_js_integration is a simple example to demonstrate the interaction between JS and Native. In a project, it implements three cefapps: ClientAppBrowser, ClientAppRenderer, and ClientAppOther, which correspond to Browser, Render, and other processes.

Interaction between JS and Native code occurs in the Render process. The App must inherit the CefRenderProcessHandler to integrate JS-related functions. Therefore, when the application starts, it determines the process type and creates different cefapps based on different process types.

This example demonstrates three JS interaction methods (see https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md ):
-Use CefFrame: ExecuteJavaScript () in native code to execute JavaScript code
-Bind a function or object to the window object corresponding to CefFrame. JS Code accesses the function or object exported by native code through the window object.
-Use CefRegisterExtension () to register JS extensions. JS directly accesses the objects registered to JS Context.

For this project, refer to cefsimple, cefclient, and slave.

Well, the background is similar. Go to the source code.

Cef_js_integration.cpp:

#include 
  
   #include 
   
    #include "cef_js_integration.h"#include 
    
     #include #include "include/cef_app.h"#include "include/cef_browser.h"#include "ClientAppBrowser.h"#include "ClientAppRenderer.h"#include "ClientAppOther.h"#include "include/cef_command_line.h"#include "include/cef_sandbox_win.h"//#define CEF_USE_SANDBOX 1#if defined(CEF_USE_SANDBOX)#pragma comment(lib, "cef_sandbox.lib")#endifint APIENTRY _tWinMain(_In_ HINSTANCE hInstance,                     _In_opt_ HINSTANCE hPrevInstance,                     _In_ LPTSTR    lpCmdLine,                     _In_ int       nCmdShow){    UNREFERENCED_PARAMETER(hPrevInstance);    UNREFERENCED_PARAMETER(lpCmdLine);    // Enable High-DPI support on Windows 7 or newer.    CefEnableHighDPISupport();    CefMainArgs main_args(hInstance);    void* sandbox_info = NULL;#if defined(CEF_USE_SANDBOX)    CefScopedSandboxInfo scoped_sandbox;    sandbox_info = scoped_sandbox.sandbox_info();#endif    // Parse command-line arguments.    CefRefPtr
     
       command_line = CefCommandLine::CreateCommandLine();    command_line->InitFromString(::GetCommandLineW());    // Create a ClientApp of the correct type.    CefRefPtr
      
        app; // The command-line flag won't be specified for the browser process. if (!command_line->HasSwitch("type")) { app = new ClientAppBrowser(); } else { const std::string& processType = command_line->GetSwitchValue("type"); if (processType == "renderer") { app = new ClientAppRenderer(); } else { app = new ClientAppOther(); } } // Execute the secondary process, if any. int exit_code = CefExecuteProcess(main_args, app, sandbox_info); if (exit_code >= 0) return exit_code; // Specify CEF global settings here. CefSettings settings;#if !defined(CEF_USE_SANDBOX) settings.no_sandbox = true;#endif // Initialize CEF. CefInitialize(main_args, settings, app.get(), sandbox_info); // Run the CEF message loop. This will block until CefQuitMessageLoop() is // called. CefRunMessageLoop(); // Shut down CEF. CefShutdown(); return 0;}
      
     
    
   
  

As you can see, the _ tWinMain method parses the command line parameters and creates different cefapps based on the process type. This is the difference between it and cefsimple.

The ClientAppBrowser class is basically the same as SimpleApp in the cefsimple example, skipped.

The ClientAppRender class is implemented in ClientAppRender. h and ClientAppRender. cpp. ClientAppRender. h:

#ifndef CEF3_CLIENT_APP_RENDERER_H#define CEF3_CLIENT_APP_RENDERER_H#include "include/cef_app.h"#include "include/cef_client.h"#include "V8handler.h"class ClientAppRenderer : public CefApp,    public CefRenderProcessHandler {public:    ClientAppRenderer();    CefRefPtr
  
    GetRenderProcessHandler() OVERRIDE    {        return this;    }    void OnContextCreated(        CefRefPtr
   
     browser,        CefRefPtr
    
      frame,        CefRefPtr
     
       context);    void OnWebKitInitialized() OVERRIDE;private:    CefRefPtr
      
        m_v8Handler; IMPLEMENT_REFCOUNTING(ClientAppRenderer);};#endif 
      
     
    
   
  

ClientAppRender aggregates instances of the ClientV8Handler class. First, let's look at the OnContextCreated and OnWebKitInitialized of ClientAppRender. They are the key to implementing interaction between JS and Native. The Code is as follows:

#include "ClientAppRenderer.h"#include "V8handler.h"#include 
  
   #include 
   
    ClientAppRenderer::ClientAppRenderer()    : m_v8Handler(new ClientV8Handler){}void ClientAppRenderer::OnContextCreated(CefRefPtr
    
      browser,    CefRefPtr
     
       frame,    CefRefPtr
      
        context){ OutputDebugString(_T("ClientAppRenderer::OnContextCreated, create window binding\r\n")); // Retrieve the context's window object. CefRefPtr
       
         object = context->GetGlobal(); // Create the "NativeLogin" function. CefRefPtr
        
          func = CefV8Value::CreateFunction("NativeLogin", m_v8Handler); // Add the "NativeLogin" function to the "window" object. object->SetValue("NativeLogin", func, V8_PROPERTY_ATTRIBUTE_NONE);}void ClientAppRenderer::OnWebKitInitialized(){ OutputDebugString(_T("ClientAppRenderer::OnWebKitInitialized, create js extensions\r\n")); std::string app_code = "var app;" "if (!app)" " app = {};" "(function() {" " app.GetId = function() {" " native function GetId();" " return GetId();" " };" "})();"; CefRegisterExtension("v8/app", app_code, m_v8Handler);}
        
       
      
     
    
   
  

OnContextCreated binds a NativeLogin function to the window object. This function will be handled by the ClientV8Handler class. When JS Code in HTML calls window. NativeLogin, The ClientV8Handler Execute method will be called.

OnWebKitInitialized registers a JS extension named app, which defines the GetId method for the app, and the app. GetId internally calls the native version of GetId (). The JS Code in HTML may be as follows:

alert(app.GetId());

When the Browser executes the above Code, the ClientV8Handler Execute method will be called.

Now let's take a look at the implementation of ClientV8Handler (V8Handler. cpp ):

#include "V8handler.h"#include 
  
   #include 
   
    bool ClientV8Handler::Execute(const CefString& name,    CefRefPtr
    
      object,    const CefV8ValueList& arguments,    CefRefPtr
     
      & retval,    CefString& exception) {    if (name == "NativeLogin")     {        if (arguments.size() == 2)        {            CefString strUser = arguments.at(0)->GetStringValue();            CefString strPassword = arguments.at(1)->GetStringValue();            TCHAR szLog[256] = { 0 };            _stprintf_s(szLog, 256, _T("user - %s, password - %s\r\n"), strUser.c_str(), strPassword.c_str());            OutputDebugString(szLog);            //TODO: doSomething() in native way            retval = CefV8Value::CreateInt(0);        }        else        {            retval = CefV8Value::CreateInt(2);        }        return true;    }    else if (name == "GetId")     {        if (arguments.size() == 0)         {            // execute javascript             // just for test            CefRefPtr
      
        frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame(); frame->ExecuteJavaScript("alert('Hello, I came from native world.')", frame->GetURL(), 0); // return to JS retval = CefV8Value::CreateString("72395678"); return true; } } // Function does not exist. return false;}
      
     
    
   
  

When processing the GetId method, Execute also uses CefFrame: ExecuteJavaScript to demonstrate how to Execute JS Code in native code.

Finally, let's take a look at the html code:

<Script type = "text/javascript"> function Login () {window. nativeLogin (document. getElementById ("userName "). value, document. getElementById ("password "). value);} function GetId () {alert ("get id from native by extensions:" + app. getId () ;}</script> Call into native by Window bindings:
<form><code>UserName: <input id="userName" type="text" />&nbsp;&nbsp;Password: <input id="password" type="text" />&nbsp;&nbsp;<input onclick="Login()" type="button" value="Login" /> </code></form>
Call into native by js extensions:

Run the following command to test:

cef_js_integration.exe --url=file:///cef_js_integration.html

Well, the interaction between JS and Native is now available.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.