写点什么

CEF | CEF 浏览器客户端功能扩展:实现与 JS 交互的功能

作者:YOLO.
  • 2022-10-20
    河北
  • 本文字数:3435 字

    阅读完需:约 1 分钟

CEF | CEF浏览器客户端功能扩展:实现与JS交互的功能

背景

之前的文章已经基本实现了浏览器的常用功能,如网页的加载、跳转、前进、后退、刷新、控制台窗口、设置 cookie 等。


除了这些基本功能,我们自定义浏览器客户端时,有时是需要与自己的网页进行交互的,比如我们的客户端要调用网页的某个方法、或者网页需要调用客户端程序的某个方法。这时我们就需要实现浏览器客户端与 js 的交互功能。


这里依然是在 CefSimple 示例的基础上进行的拓展,可以结合之前的文章一起看。

具体实现

cef 调用 js 方法

首先,获取浏览器窗口类。 参见:https://juejin.cn/post/7120536782281637918


在 simple_handler.h 文件中有一个浏览器窗口列表变量,里面存储了所有创建了的 CefBrowser,所以我们想要获取浏览器窗口类,就需要在 simple_handler.h 文件中添加一个 getBrowser()函数,返回一个 CefBrowser。


//simple_handler.hCefRefPtr<CefBrowser> getBrowser();
复制代码


//simple_handler.cpp//如果集合不为空,获取集合中的第一个CefBrowser元素。CefRefPtr<CefBrowser> SimpleHandler::getBrowser(){    if(!browser_list_.empty())     {         return  browser_list_.front();    }    return NULL;}
复制代码


然后,在自定义 Widget 中进行调用。


CEF 有专门的调用 js 方法的函数:ExecuteJavaScript,它是一个属于 CefFrame 类的方法。所以我们想要调用 js 的方法,只需要获取到页面的 frame,然后调用 ExecuteJavaScript 就可以了。


参数一是要执行的 js 语句;参数二是可以找到问题脚本(如果有的话)的 URL。渲染器可能会请求这个 URL 来向开发人员显示错误的来源;第三个参数是用于错误报告的基线编号。


void CefBrowserWidget::ExecuteJS(QString js){    if(m_cefHandler->getBrowser())    {        CefRefPtr<CefFrame> frame = m_cefHandler->getBrowser()->GetMainFrame();        if (frame)        {            frame->ExecuteJavaScript(js.toStdWString(), frame->GetURL(), 0);        }    }}
复制代码

js 调用 cef 方法

js 调用 cef 有两种方法,一种是窗口绑定,一种是 js 扩展。


窗口绑定是在 CefRenderProcessHandler::OnContextCreated 创建 V8 对象,将 V8 对象注册到 context 中;


js 拓展是在 CefRenderProcessHandler::OnWebKitInitialized 中注册新的 V8 扩展关联指定的 js。


我们这里用的是第一种方法。


- 首先,修改 SimpleApp.h,让 SimpleApp 继承 CefRenderProcessHandler,并实现 OnContextCreated 方法。


class SimpleApp :        public QObject,        public CefApp,        public CefBrowserProcessHandler,        public CefRenderProcessHandler{        ...    virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE {        return this;}            // CefRenderProcessHandler methods:    virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,                                  CefRefPtr<CefFrame> frame,                                  CefRefPtr<CefV8Context> context) OVERRIDE;        ...};
复制代码


然后,实现 OnContextCreated 方法。


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.     //ClientV8Handler就是我们要创建的V8对象,这个类是我们自定义的一个类,见下一步。    ClientV8Handler* handler = new ClientV8Handler();        // Create the "myfunc" function.这里的recvRenderMsg就是js调的方法名。    CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("recvRenderMsg", handler);        // Add the "myfunc" function to the "window" object.    object->SetValue("recvRenderMsg", func, V8_PROPERTY_ATTRIBUTE_NONE);}
复制代码


定义 ClientV8Handler 类,实现接收调用。


#include <QObject>#include "include/cef_v8.h"
class ClientV8Handler : public QObject, public CefV8Handler{ Q_OBJECTpublic: ClientV8Handler(); virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE;private: // Provide the reference counting implementation for this class. IMPLEMENT_REFCOUNTING(ClientV8Handler);};
复制代码


这里最重要的就是 Execute 方法的实现。


bool ClientV8Handler::Execute(const CefString &name, CefRefPtr<CefV8Value> object,                               const CefV8ValueList &arguments,                               CefRefPtr<CefV8Value> &retval,                               CefString &exception){    // 判断js调用的方法名称    if (name == "recvRenderMsg")    {        // 构造消息        CefRefPtr<CefProcessMessage> recvRenderMsg =CefProcessMessage::Create("recvRenderMsg");                // 该方法的参数,是以列表形式获取的        CefRefPtr<CefListValue> args = recvRenderMsg->GetArgumentList();        args->SetSize(2);        QString funcName = QString::fromStdWString(arguments.at(0)->GetStringValue());        QString funcValue = QString::fromStdWString(arguments.at(1)->GetStringValue());        args->SetString(0, funcName.toStdWString());        args->SetString(1, funcValue.toStdWString());                // 发送消息        CefV8Context::GetCurrentContext()->GetBrowser()->GetFocusedFrame()        ->SendProcessMessage(PID_BROWSER, recvRenderMsg);                // 该方法的返回值        //retval = CefV8Value::CreateString("recvRenderMsg!");        return true;    }        // Function does not exist.    return false;}
复制代码


这里需要注意的是 CefBrowserProcess 和 CefRenderProcess 是 cef 的两个进程,之前我们实现过得功能全部都在 CefBrowserProcess 进程中,而 js 调用 cef 方法的功能是在 CefRenderProcess 进程中。这是两个单独的进程,但是都是通过 SimpleApp 类继承并实现的。但是它们两个进程之间是互不相同的,需要通过发送消息的方式实现数据交换,即 SendProcessMessage。然后在 CefBrowserProcess 进程中进行消息的接收。


所以在程序运行时,你再 Execute 中执行 qDebug,是不会有输出显示在输出栏中的。想要通过发送信号的方式也是不行的。


接收消息。


在 simple_handler.h 文件中添加 OnProcessMessageReceived。


virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,                                          CefRefPtr<CefFrame> frame,                                          CefProcessId source_process,                                          CefRefPtr<CefProcessMessage> message) OVERRIDE;
复制代码


在 simple_handler.cpp 中进行实现。


bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,                                              CefRefPtr<CefFrame> frame,                                              CefProcessId source_process,                                             CefRefPtr<CefProcessMessage> message){    const std::string& messageName = message->GetName();    if (messageName == "recvRenderMsg") // 消息的名称    {        // 获取消息的参数        CefRefPtr<CefListValue> args = message->GetArgumentList();        CefString funcName = args->GetString(0);        CefString funcValue = args->GetString(1);                // 这个时候可以发送消息给想要接收的地方了        emit sigMsg(QString::fromStdWString(funcName), QString::fromStdWString(funcValue));        return true;    }    return false;}
复制代码


这样就实现了 cef 与 js 的全部交互功能。

发布于: 刚刚阅读数: 3
用户头像

YOLO.

关注

还未添加个人签名 2022-05-06 加入

还未添加个人简介

评论

发布
暂无评论
CEF | CEF浏览器客户端功能扩展:实现与JS交互的功能_qt_YOLO._InfoQ写作社区