CEF中C++与JS交互

转载原文:https://blog.csdn.net/leapmotion/article/details/80853508

 

https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md#markdown-header-js-functions

上记录了三种C++与JS交互方式,以下我来讨论下。

首先引用下博客中的关于CEF架构的图

参考博客:

https://blog.csdn.net/foruok/article/details/50573612

https://blog.csdn.net/chenlycly/article/details/53355670

https://blog.csdn.net/swimming_in_it_/article/details/78869549

1. C++调用JS

1)ExecuteJavaScript

最简单的C++调用JS方法,需要Browser的对象,我这里调用一般是在CefClient的实现类中使用,但是有个缺点就是执行函数获得不了返回值。

    void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
                                      const CefString& title) {
      CEF_REQUIRE_UI_THREAD();
     
      CefRefPtr<CefFrame> frame = browser->GetMainFrame();
      frame->ExecuteJavaScript("sayHello()", frame->GetURL(), 0);

这里是在OnTitleChange调用ExecuteJavaScript,调用sayHello函数是在JS代码里声明的。

该类的声明是:

    class SimpleHandler : public CefClient,
                          public CefDisplayHandler,
                          public CefLifeSpanHandler,
                          public CefLoadHandler

而OnTitleChange是SimpleHandler重写CefDisplayHandler里的虚函数,在标题改变时调用OnTitleChange函数。

JS代码:

    <script language = "JavaScript">
        document.title = 'test js C++ communication';
     
        window.setTimeout(function() {
            document.title = 'C++ and JS';
        }, 5000);
     
        function sayHello() {
            alert("Hello!");
        }
    </script>

JS代码里设置过5秒后修改标题,然后在OnTitleChange函数调用sayHello()

 

2. JS调用C++

1) Window Binding

这种方式就是在C++里声明的对象或函数绑定到JS中的window对象上,这样在JS代码使用window对象调用。

这种需要在一个类继承CefApp和CefRenderProcessHandler,官方建议重新写一个类,而不是与继承

CefBrowserProcessHandler写成同一个类,具体情况我也不知道,但是CefRenderProcessHandler的函数是在一个进程,

CefBrowserProcessHandler的函数是另一个进程,而我试试同时继承两个类(SimpleApp),然后在CefInitialize启动这各类

SimpleApp对象, 但是继承自CefRenderProcessHandler的函数是没有在执行,具体还可以请大神赐教。然后我就写了另个类

RenderApp,将原来的SimpleApp改写成BrowerApp,RenderApp继承CefApp和CefRenderProcessHandler,BrowerApp

继承CefApp和CefBrowserProcessHandler中。

我们把重点放在RenderApp,JS交互C++是在RenderApp实现,Window Binding实现方式是在 OnContextCreated,如下:

    void RenderApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
                         CefRefPtr<CefFrame> frame,
                     CefRefPtr<CefV8Context> context)
    {
        CefRefPtr<CefV8Value> object = context->GetGlobal();
     
        CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value");
        object->SetValue("myvalue", str, V8_PROPERTY_ATTRIBUTE_NONE);
     
        CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction("loadStuList", m_V8Handler);
        object->SetValue("loadStuList", function, V8_PROPERTY_ATTRIBUTE_NONE);
    }

使用context获得对象,然后将object获function绑定到这个对象上,V8是一个JS引擎,V8引擎是一个JavaScript引擎实现,最

初由一些语言方面专家设计,后被谷歌收购,随后谷歌对其进行了开源。V8使用C++开发,,在运行JavaScript之前,相比其它

的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用

了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。

V8支持众多操作系统,如windows、linux、android等,也支持其他硬件架构,如IA32,X64,ARM等,具有很好的可移植和跨平

台特性。(见:https://blog.csdn.net/swimming_in_it_/article/details/78869549)。CefV8Value是一个类似的万能类型,

具体可以参考源码。上图是绑定了string对象和function(loadStuList),然后在JS代码里调用就好了。如下:

    <script language = "JavaScript">
        document.title = 'test js C++ communication';
     
        function sayValue() {
            alert(window.myvalue);
        }
     
        function sayUserName() {
            alert(window.loadStuList('Lily', '123456'));
        }
    </script>

只需要在JS里调用sayValue和sayUserName函数就会到C++里执行,那究竟是在C++里怎样执行呢?回到OnContextCreated

函数里,CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction("loadStuList", m_V8Handler)中有

m_V8Handler,所以JS调用C++的函数执行是和m_V8Handler有关,我是这样声明的:

CefRefPtr<ClientV8Handler> m_V8Handler;

ClientV8Handler的声明是:

    class ClientV8Handler : public CefV8Handler
    {
    public:
        virtual bool Execute(const CefString& name,
                     CefRefPtr<CefV8Value> object,
                     const CefV8ValueList& arguments,
                     CefRefPtr<CefV8Value>& retval,
                     CefString& exception) OVERRIDE;
     
    private:
        IMPLEMENT_REFCOUNTING(ClientV8Handler)
    };

而调用C++就是在Execute中:

    bool ClientV8Handler::Execute(const CefString& name,
        CefRefPtr<CefV8Value> object,
        const CefV8ValueList& arguments,
        CefRefPtr<CefV8Value>& retval,
        CefString& exception)
    {
     
     
        if (name == "loadStuList") {
            if (arguments.size() == 2) {
                CefString strUser = arguments.at(0)->GetStringValue();
                CefString strPass = arguments.at(1)->GetStringValue();
     
     
                retval = CefV8Value::CreateString(strPass);
            }
            else {
                retval = CefV8Value::CreateInt(2);
            }
     
     
            return true;
        }
     
        return false;
    }

当JS调用loadStuList时就会到这个函数,retval是loadStuList的返回值等等。

2)Extensions

这种方式很像window binding,不同点是可以将extensions记载到任意一个Frame且不能改变。注意的是加载extensions时

DOM对象还没生成,就是说extensions的代码不能使用DOM对象。该方式使用是在RenderApp的OnWebKitInitialized中实现

    void RenderApp::OnWebKitInitialized()
    {
        std::string app_code =
            "var app;"
            "if (!app)"
            "    app = {};"
            "(function() {"
            "    app.GetId = function(x) {"
            "        native function GetId(x);"
            "    };"
            "})();";
     
        CefRegisterExtension("v8app", app_code, m_V8Handler);
    }

上边的app_code是js代码,意思是在C++给JS声明,然后在JS中调用就好,以上app_code意思是声明app对象,为该类声明

GetId函数,在JS代码中调用是:

    <script language = "JavaScript">
        document.title = 'test js C++ communication';
     
        function sayExtensions() {
            alert(app.GetId(2));
        }
    </script>

在ClientV8Handler实现是:

    else if (name == "GetId") {
        CefRefPtr<CefFrame> frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
        frame->ExecuteJavaScript("alert('Hello from extension');", frame->GetURL(), 0);        
            
        retval = CefV8Value::CreateInt(arguments.size());
        return true;
    }

写到这里发现有点不对劲,怎么全是JS调用C++, 而C++调用JS只有ExecuteJavaScript,但是拿不到返回值,这很奇怪,不过可

以在JS里设置回调函数,然后在C++里需要的时候调用这个回调函数就能达到C++调用JS了

 

最后,就是启动这个RenderApp就好了,如下:

    CefRefPtr<RenderApp> renderApp(new RenderApp);
    int exit_code = CefExecuteProcess(main_args, renderApp.get(), sandbox_info);
    if (exit_code >= 0) {
        // The sub-process has completed so return here.
        return exit_code;
    }

CefExecuteProcess这个函数时用来启动子进程的,所以将RenderApp启动是在这个函数里。

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值