导航:[首页]->[cpp]->[Libcef CPP导出函数给js]

注册回调

为了导出函数,我们需要重新实现GetRenderProcessHandler函数,以便接收OnContextCreated消息。

class SimpleApp: public CefApp,
        public CefBrowserProcessHandler,
        public CefRenderProcessHandler{
public:
    SimpleApp();
    virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE {
        return this;
    }

    virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE;
}


void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
    // Retrieve the context's window object.
    CefRefPtr<CefV8Value> object = context->GetGlobal();

    // Create an instance of my CefV8Handler object.
    CefRefPtr<CefV8Handler> handler = new MyV8Handler();

    // Create the "myfunc" function.
    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);

    // Add the "myfunc" function to the "window" object.
    object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}

在注册的CefV8Handler对象的Execute函数中,第三个函数表示js传过来的参数集合。

CefV8Value是个通用对象,既可以表示整形,字符串,也可以表示函数,对象等复杂结构

若是个函数,我们可以调用ExecuteFunction函数来执行这个js函数。不过同样需要保留第二个参数object已标示哪个会话

class MyV8Handler: public CefV8Handler
{
public:
    MyV8Handler()
    {
    }

    virtual bool Execute(const CefString& name,
            CefRefPtr<CefV8Value> object,
            const CefV8ValueList& arguments,
            CefRefPtr<CefV8Value>& retval,
            CefString& exception) OVERRIDE
    {
        if (name == "myfunc")
        {

            std::ostringstream strbuf_;
            strbuf_ << name.ToString() << " | " << arguments.size() << " | "
                    << object->GetStringValue().ToString() << " | ";
            size_t size_ = arguments.size() - 1;
            for (size_t i = 0; i < size_; i++)
            {
                CefRefPtr<CefV8Value> tmparg_ = arguments[i];
                strbuf_ << tmparg_->GetStringValue().ToString() << " | ";
            }
            strbuf_ << "omg,fuck you";

            // Return my string value.
            retval = CefV8Value::CreateString(strbuf_.str().c_str());

            // 最后执行最后一个参数传入的函数对象
            CefRefPtr<CefV8Value> tmparg_ = arguments[arguments.size() - 1];
            CefV8ValueList arglist_;
            tmparg_->ExecuteFunction(object,arglist_);
            return true;
        }

        // Function does not exist.
        return false;
    }

    // Provide the reference counting implementation for this class.
    IMPLEMENT_REFCOUNTING(MyV8Handler)
    ;
};

<script type="text/javascript" src="jquery-2.1.1.min.js"></script>
<script type="text/javascript">
    $(function(){
        //alert("fuck1");
        $("#fuck").click(function(){
            $("#fuck").text(window.myfunc("a","b","c",function(){
                $("#fuck2").text("fuck you");
            }));
        });
    });
</script>

异步回调

在下面的js中,我们传入两个回调函数(假设需求是搜索的音乐文件,第一个回调用于刷新文件列表,第二个用于通知结束)。在调用后立即返回,而c++的代码再异步执行扫描并回调传入的js函数。

$(function(){
    $("#submit").click(function(){
    $("#status").text("process");
    var index=0
    window.myfunc(function(path){
        var str = "<li>" + index + ":" + path + "</li>";
        index++;
        $("#files").children().last().append(str);
    },
    function(){
        $("#status").text("");
    });
    });
});

接前面的实现,我们新增一个后台线程类

class MyV8Handler;
class Test
{
public:
    void test()
    {
        // 最后执行最后一个参数传入的函数对象
        for(int i = 0;i<10;i++)
        {
            CefString path_;
            path_.FromString("fuck");
            CefPostTask(TID_RENDERER, NewCefRunnableMethod(
                m_ptr_,
                &MyV8Handler::Updatefile,
                context,
                object,
                m_path_func_,
                path_));
            sleep(1);
        }
        {
            CefPostTask(TID_RENDERER, NewCefRunnableMethod(
                m_ptr_,
                &MyV8Handler::UpdatefileFini,
                context,
                object,
                m_fin_func_));
        }
    }

    static void* Thread(void* arg)
    {
        Test* ptr_ = (Test*)(arg);
        ptr_->test();
        return NULL;
    }

    MyV8Handler* m_ptr_;
    CefRefPtr<CefV8Context> context;
    CefRefPtr<CefV8Value> object;
    CefRefPtr<CefV8Value> m_path_func_;
    CefRefPtr<CefV8Value> m_fin_func_;
};

如果是直接调用,我们可以不理会CefV8Context,异步时必须保留并使用它来调用ExecuteFunctionWithContext

class MyV8Handler: public CefV8Handler
{
public:
    MyV8Handler(CefRefPtr<CefBrowser> browser):
        m_browser_(browser)
    {
    }

    void Updatefile(
            CefRefPtr<CefV8Context> context,
            CefRefPtr<CefV8Value> object,
            CefRefPtr<CefV8Value> func,
            const CefString& path)
    {
        CefRefPtr<CefV8Value> arg1_ = CefV8Value::CreateString(path);

        CefV8ValueList arguments;
        arguments.push_back(arg1_);

        func->ExecuteFunctionWithContext(context,object,arguments);
    }
    void UpdatefileFini(
            CefRefPtr<CefV8Context> context,
            CefRefPtr<CefV8Value> object,
            CefRefPtr<CefV8Value> func)
    {
        CefV8ValueList arguments;
        func->ExecuteFunctionWithContext(context,object,arguments);
    }

    virtual bool Execute(const CefString& name,
            CefRefPtr<CefV8Value> object,
            const CefV8ValueList& arguments,
            CefRefPtr<CefV8Value>& retval,
            CefString& exception) OVERRIDE
    {
        if (name == "myfunc")
        {
            pthread_t thread_;
            Test* test_ = new Test();
            test_->object = object;
            test_->m_ptr_ = this;
            test_->m_path_func_ = arguments[0];
            test_->m_fin_func_ = arguments[1];
            test_->context = CefV8Context::GetCurrentContext();
            pthread_create(&thread_,NULL,Test::Thread,(void*)test_);

            // Return my string value.
            retval = CefV8Value::CreateString("fuck");

            return true;
        }

        // Function does not exist.
        return false;
    }

线程

我们从后台线程给主线程发送任务时,注意是TID_RENDERER,因为以上的逻辑都在Render进程中运行,若需要通知Browser进程处理,需要通过消息通知(IPC)。

  1. TID_UI thread is the main thread in the browser process. This will be the same as the main application thread if CefInitialize() is called with a CefSettings.multi_threaded_message_loop value of false.
  2. TID_IO thread is used in the browser process to process IPC and network messages.
  3. TID_FILE thread is used in the browser process to interact with the file system.
  4. TID_RENDERER thread is the main thread in the renderer process.

多进程IPC

libcef提供了一种机制,用于在渲染进程(Render Process)和浏览器进程(Browser Process)之间通信,这常见于要控制原生窗口的行为,例如在Web上实现标题栏拖动,最大化,最小化等

A message sent from the browser process to the render process will arrive in CefRenderProcessHandler::OnProcessMessageReceived(). A message sent from the render process to the browser process will arrive in CefClient::OnProcessMessageReceived().

Use PID_BROWSER instead when sending a message to the browser process.

// Create the message object.
CefRefPtr<CefProcessMessage> msg= CefProcessMessage::Create("my_message");

// Retrieve the argument list object.
CefRefPtr<CefListValue> args = msg->GetArgumentList();

// Populate the argument values.
args->SetString(0, "my string");
args->SetInt(0, 10);

// Send the process message to the render process.
// Use PID_BROWSER instead when sending a message to the browser process.
m_browser_->SendProcessMessage(PID_BROWSER, msg);

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

    bool OnProcessMessageReceived(
            CefRefPtr<CefBrowser> browser,
            CefProcessId source_process,
            CefRefPtr<CefProcessMessage> message)
    {
        const std::string& message_name = message->GetName();
        if (message_name == "my_message")
        {
        }
    }
}

///
// Implement this interface to provide handler implementations.
///
/*--cef(source=client,no_debugct_check)--*/
class CefClient: public virtual CefBase
{
    ///
    // Called when a new message is received from a different process. Return true
    // if the message was handled or false otherwise. Do not keep a reference to
    // or attempt to access the message outside of this callback.
    ///
    /*--cef()--*/
    virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
            CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
    {
        return false;
    }
};

///
// Class used to implement render process callbacks. The methods of this class
// will be called on the render process main thread (TID_RENDERER) unless
// otherwise indicated.
///
/*--cef(source=client)--*/
class CefRenderProcessHandler: public virtual CefBase
{
    ///
    // Called when a new message is received from a different process. Return true
    // if the message was handled or false otherwise. Do not keep a reference to
    // or attempt to access the message outside of this callback.
    ///
    /*--cef()--*/
    virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
            CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
    {
        return false;
    }
};

https://code.google.com/p/chromiumembedded/wiki/GeneralUsage#Asynchronous_Bindings提到一种message_router的IPC(Generic Message Router),个人感觉没啥用

Starting with trunk revision 1574 CEF provides a generic implementation for routing asynchronous messages between JavaScript running in the renderer process and C++ running in the browser process. An application interacts with the router by passing it data from standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved, OnContextCreated, etc). The renderer-side router supports generic JavaScript callback registration and execution while the browser-side router supports application-specific logic via one or more application-provided Handler instances.

强制单进程

  1. 在运行程序加入命令行--single-process
  2. 在程序中设置CefSettings的single_process属性

CEF3 supports a single-process run mode for debugging purposes via the "--single-process" command-line flag. Platform-specific debugging tips are also available for Windows, Mac OS X and Linux.

// Specify CEF global settings here.
CefSettings settings;
settings.single_process = 1;

参考

  1. http://www.iwebrtc.com/blog/chromium-ebedded-framework-javascript-integration/
  2. https://code.google.com/p/chromiumembedded/wiki/GeneralUsage
  3. https://code.google.com/p/chromiumembedded/wiki/JavaScriptIntegration