CEF3:与 JavaScript 整合(二)

下载工程

本篇续:CEF3:与 JavaScript 整合(一)

本篇讲解如何通过消息路由,将 JS 发送的请求路由到浏览器进程来处理。

消息路由

消息路由发生在浏览器进程和渲染进程之间,通过消息路由,CEF将运行在渲染进程的 JS 消息发送到浏览器进程,并在浏览器进程中通过执行 C++ 代码做出响应。整个过程都是异步进行的。

实现步骤

CEF 抽象出了两个类来分别表示路由的两端:

  • CefMessageRouterBrowserSide 表示路由的浏览器进程端
  • CefMessageRouterRendererSide 表示路由的渲染进程端

在实现消息路由的具体实现中,需要在浏览器进程端创建 CefMessageRouterBrowserSide 对象,在渲染进程端创建 CefMessageRouterRendererSide。具体的实现步骤如下:

  1. 浏览器端:

    1.1. 实现 RouterHandler,重写它的 OnQuery 方法以处理 JS 请求。
    1.2. 在 CefClient 中声明 CefMessageRouterBrowserSide 对象,在 CefLifeSpanHandler::OnAfterCreated() 中创建它,并向它注册第1步中实现的处理类。
    1.3. 在 CefLifeSpanHandler::OnBeforeClose() 中销毁 CefMessageRouterBrowserSide 对象和处理对象。
    1.4. 重写 CefClient::OnProcessMessageReceived(),CefRequestHandler::OnBeforeBrowse(), CefRequestHandler::OnRenderProcessTerminated(),在这三个方法中调用 CefMessageRouterBrowserSide 对象同名的方法。

  2. 渲染端:

    2.1. 在 CefApp 中声明 CefMessageRouterRendererSide 对象,在 CefRenderProcessHandler::OnWebKitInitialized() 中创建它。
    2.2. 重写 CefRenderProcessHandler::OnContextCreated(),CefRenderProcessHandler::OnContextReleased(),CefRenderProcessHandler::OnProcessMessageReceived(),在这三个方法中调用 CefMessageRouterRendererSide 对象同名的方法。

示例代码

#include "include/cef_app.h"
#include "include/cef_browser.h"
#include "include/cef_client.h"
#include "include/wrapper/cef_message_router.h"
#include "include/wrapper/cef_helpers.h"
#include <Windows.h>

// 浏览器客户端
class BrowserClient : public CefClient, public CefLifeSpanHandler, public CefRequestHandler
{
public:
    // 1.1
    class RouterHandler : public CefMessageRouterBrowserSide::Handler
    {
    public:
        virtual bool OnQuery(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
            int64 query_id, const CefString& request, bool persistent, 
            CefRefPtr<Callback> callback) override
        {
            CEF_REQUIRE_UI_THREAD();

            // 处理JS请求
            if (request == "giveMeFive") 
            {
                callback->Success("OK! Give you 5!");
                return true;
            }

            return false;
        }
    };

    // Constructor & Destructor
public:
    BrowserClient() {}
    virtual ~BrowserClient() {}

    // CefClient methods:
public:
    virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }

    virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }

    // 1.4
    virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
        CefProcessId source_process, CefRefPtr<CefProcessMessage> message) override
    {
        return m_messageRouter->OnProcessMessageReceived(browser, source_process, message);
    }

    // CefLifeSpanHandler methods:
public:
    // 1.2
    virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser)
    {
        CEF_REQUIRE_UI_THREAD();

        if (!m_messageRouter)
        {
            CefMessageRouterConfig config;
            m_messageRouter = CefMessageRouterBrowserSide::Create(config);
            m_messageRouter->AddHandler(m_routerHandler.get(), true);
        }
    }

    // 1.3
    virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
    {
        CEF_REQUIRE_UI_THREAD();

        m_messageRouter->RemoveHandler(m_routerHandler.get());
        m_routerHandler.reset();
        m_messageRouter = nullptr;

        CefQuitMessageLoop();
    }

    // CefRequestHandler methods:
public:
    // 1.4
    virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, 
        CefRefPtr<CefRequest> request, bool is_redirect) override
    {
        CEF_REQUIRE_UI_THREAD();

        m_messageRouter->OnBeforeBrowse(browser, frame);
        return false;
    }

    // 1.4
    virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
        TerminationStatus status) override
    {
        CEF_REQUIRE_UI_THREAD();

        m_messageRouter->OnRenderProcessTerminated(browser);
    }

private:
    std::unique_ptr<RouterHandler> m_routerHandler{ new RouterHandler };
    // 1.2
    CefRefPtr<CefMessageRouterBrowserSide> m_messageRouter{ nullptr };

private:
    IMPLEMENT_REFCOUNTING(BrowserClient);
    DISALLOW_COPY_AND_ASSIGN(BrowserClient);
};

// 浏览器进程 App
class AppBrowser : public CefApp, public CefBrowserProcessHandler
{
public:
    AppBrowser() {}
    virtual ~AppBrowser() {}

    // CefApp methods:
    virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override { return this; }

    // CefBrowserProcessHandler methods:
public:
    virtual void OnContextInitialized() override
    {
        CEF_REQUIRE_UI_THREAD();

        CefWindowInfo window_info;
        CefRefPtr<BrowserClient> client(new BrowserClient());
        window_info.SetAsPopup(NULL, "cefsimple");
        CefBrowserSettings browser_settings;
        CefString url = "file:///G:/projects/vs2015/SimpleBrowser/JSTest/test.html";
        CefBrowserHost::CreateBrowser(window_info, client, url, browser_settings, NULL);
    }

private:
    IMPLEMENT_REFCOUNTING(AppBrowser);
    DISALLOW_COPY_AND_ASSIGN(AppBrowser);
};

// 渲染进程 App
class AppRenderer : public CefApp, public CefRenderProcessHandler
{
public:
    AppRenderer() {}
    virtual ~AppRenderer() {}

    // CefApp methods:
public:
    virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override { return this; }

    // CefRenderProcessHandler methods;
public:
    // 2.1
    virtual void OnWebKitInitialized() override
    {
        CEF_REQUIRE_RENDERER_THREAD();

        CefMessageRouterConfig config;
        m_messageRouter = CefMessageRouterRendererSide::Create(config);
    }

    // 2.2
    virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override
    {
        CEF_REQUIRE_RENDERER_THREAD();

        m_messageRouter->OnContextCreated(browser, frame, context);
    }

    // 2.2
    virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override
    {
        CEF_REQUIRE_RENDERER_THREAD();

        m_messageRouter->OnContextReleased(browser, frame, context);
    }

    // 2.2
    virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
        CefProcessId source_process, CefRefPtr<CefProcessMessage> message) override
    {
        CEF_REQUIRE_RENDERER_THREAD();

        return m_messageRouter->OnProcessMessageReceived(browser, source_process, message);
    }

private:
    // 2.1
    CefRefPtr<CefMessageRouterRendererSide> m_messageRouter{ nullptr };

private:
    IMPLEMENT_REFCOUNTING(AppRenderer);
    DISALLOW_COPY_AND_ASSIGN(AppRenderer);
};

// 除浏览器进程,渲染进程外的其他进程 App
class AppOther : public CefApp
{
public:
    AppOther() {}
    virtual ~AppOther() {}

private:
    IMPLEMENT_REFCOUNTING(AppOther);
    DISALLOW_COPY_AND_ASSIGN(AppOther);
};

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    // 获取进程启动参数
    CefMainArgs main_args(hInstance);
    CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
    command_line->InitFromString(::GetCommandLineW());

    // 根据不同的参数,创建不同的 App 
    CefRefPtr<CefApp> app;
    if (!command_line->HasSwitch("type"))
    {
        app = new AppBrowser();
    }
    else
    {
        const std::string &processType = command_line->GetSwitchValue("type");
        if (processType == "renderer")
        {
            app = new AppRenderer();
        }
        else
        {
            app = new AppOther();
        }
    }

    // 执行子进程
    int exitCode = CefExecuteProcess(main_args, app.get(), NULL);
    if (exitCode >= 0)
    {
        return exitCode;
    }

    // 执行浏览器进程
    CefSettings settings;
    settings.no_sandbox = true;
    CefInitialize(main_args, settings, app.get(), NULL);
    CefRunMessageLoop();
    CefShutdown();

    return 0;
}

JS端:

<html>
<head>
<title>测试消息路由</title>
<script language="JavaScript">
    function giveMeFive() {
      window.cefQuery({
        request: 'giveMeFive',
        onSuccess: function(response) { alert(response); },
        onFailure: function(error_code, error_message) {}
      });
    }
</script>
</head>
<body>
<form>
    <input type="button" onclick="giveMeFive();" value="Give Me Five!">
</form>
</body>
</html>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值