CEF3研究(三)

一、Off-Screen Rendering 脱屏绘制

CEF的脱屏渲染并不创建源生的浏览器窗口,而是CEF提供主应用程序在无效区域和像素buffer里渲染,然后主应用程序通过鼠标、键盘和焦点事件通知CEF。
脱屏渲染现在不支持图层混合加速图层混合加速。脱屏渲染要窗口浏览器一样接受相同的通知,包括生命周期通知等,为了使用脱屏渲染:

  1. 实现CefRenderHandler接口,所有方法是必需的,除非另有指示。
  2. 调用CefWindowInfo::SetAsOffScreen()函数和将CefWindowInfo结构传递给CefBrowserHost::CreateBrowser()之前调用CefWindowInfo::SetTransparentPainting()方法。哪里没有父窗口传递给SetAsOffScreen,那么像Centext菜单功能就不可使用。
  3. CefRenderHandler::GetViewRect()函数获取想要获取视图的矩阵。
  4. CefRenderHandler::OnPaint()被调用,以提供一个无效区域和更新的像素buffer。CefClient应用程序使用OpenGL绘制缓冲。
  5. 调用CefBrowserHost::WasResized()重置浏览器大小。这将导致调用GetViewRect()来检索新尺寸随后调用OnPaint()。
  6. CefBrowserHost::SendXXX()函数通知浏览进程的鼠标、键盘和焦点事件
  7. CefBrowserHost::CloseBrowser()销毁浏览器

二、投递任务

在单进程的不同线程之间可以通过 CefPostTask系列函数投递任务。在目标线程中,收到的任务会以异步方式在消息循环中执行。例如:
在UI线程中执行某个类方法:CefPostTask(TID_UI, NewCefRunnableMethod(object, &MyObject::MyMethod, param1, param2));
在UI线程中执行某个方法:CefPostTask(TID_IO, NewCefRunnableFunction(MyFunction, param1, param2));
如果主机应用程序需要获取运行循环的引用,可以使用CefTaskRunner类。 
例如:获取线程的任务运行者:CefRefPtr<CefTaskRunner> task_runner = CefTaskRunner::GetForThread(TID_UI);

三、进程间通信

由于CEF3以多进程方式运行,需要在不同进程之间提供通信方式。CefBrowsert和CefFrame对象分别浏览进程和渲染进程,每个CefBrowser和CefFrame对象还有一个与之关联的惟一的ID值,将匹配两边边界过程

  • 进程启动消息  为所有渲染进程提供相同的信息在启动浏览器的过程中实现CefBrowserProcessHandler::OnRenderProcessThreadCreated(),在渲染进程中传递启动消息用CefRenderProcessHandler::OnRenderThreadCreated()
  • 进程运行消息  在进程生命周期的任何时刻使用进程消息通过CefProcessMessage类传递消息。这些消息与一个特定的浏览器实例相关联并通过CefBrowser::SendProcessMessage()函数发送。进程消息可通过CefProcessMessage::GetArgumentList()函数传递状态信息。
    如:
     
    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.
    browser->SendProcessMessage(PID_RENDERER, msg);
    从浏览进程发送的消息会到达渲染进程的 CefRenderProcessHandler::OnProcessMessageReceived()函数
    从渲染进程发送的消息会到达浏览进程的CefClient::OnProcessMessageReceived()函数,如:
    
    bool MyHandler::OnProcessMessageReceived(
        CefRefPtr<CefBrowser> browser,
        CefProcessId source_process,
        CefRefPtr<CefProcessMessage> message) {
      // Check the message name.
      const std::string& message_name = message->GetName();
      if (message_name == “my_message”) {
        // Handle the message here...
        return true;
      }
      return false;
    }
    在发送的地方使用CefFrame::GetIdentifier()函数获取窗口的唯一ID,在接受进程中获取唯一ID的窗口对象使用CefBrowser::GetFrame()函数。如:
    // Helper macros for splitting and combining the int64 frame ID value.
    #define MAKE_INT64(int_low, int_high)     ((int64) (((int) (int_low)) | ((int64) ((int) (int_high))) << 32))
    #define LOW_INT(int64_val) ((int) (int64_val))
    #define HIGH_INT(int64_val) ((int) (((int64) (int64_val) >> 32) & 0xFFFFFFFFL))
    
    // Sending the frame ID.
    const int64 frame_id = frame->GetIdentifier();
    args->SetInt(0, LOW_INT(frame_id));
    args->SetInt(1, HIGH_INT(frame_id));
    
    // Receiving the frame ID.
    const int64 frame_id = MAKE_INT64(args->GetInt(0), args->GetInt(1));
    CefRefPtr<CefFrame> frame = browser->GetFrame(frame_id);

四、异步JavaScript绑定

JavaScript通信是在渲染进程中实现,在需要频繁的和浏览进程通信。JaveScript接口使用关闭和提示本身应该以异步方式设计。

  • 通用的消息路由
    CEF在渲染进程中运行JaveScript和浏览进程中运行C++之间提供了一个通用的异步路由。应用程序从标准的C++回调((OnBeforeBrowse, OnProcessMessageRecieved, OnContextCreated, etc) 函数中通过路由传参的方式交互。渲染这边的路由支持通用的Javascript回调的注册和执行。然而浏览这边路由能过一个或多少应用程序提供的处理者实例来支持应用程序特定逻辑。
    JavaScript绑定方式:

    // Create and send a new query.
    var request_id = window.cefQuery({
        request: ‘my_request‘,
        persistent: false,
        onSuccess: function(response) {},
        onFailure: function(error_code, error_message) {}
    });
    
    // Optionally cancel the query.
    window.cefQueryCancel(request_id);

    C++处理者:

    class Callback : public CefBase {
     public:
      ///
      // Notify the associated JavaScript onSuccess callback that the query has
      // completed successfully with the specified |response|.
      ///
      virtual void Success(const CefString& response) =0;
    
      ///
      // Notify the associated JavaScript onFailure callback that the query has
      // failed with the specified |error_code| and |error_message|.
      ///
      virtual void Failure(int error_code, const CefString& error_message) =0;
    };
    
    class Handler {
     public:
      ///
      // Executed when a new query is received. |query_id| uniquely identifies the
      // query for the life span of the router. Return true to handle the query
      // or false to propagate the query to other registered handlers, if any. If
      // no handlers return true from this method then the query will be
      // automatically canceled with an error code of -1 delivered to the
      // JavaScript onFailure callback. If this method returns true then a
      // Callback method must be executed either in this method or asynchronously
      // to complete the query.
      ///
      virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
                           CefRefPtr<CefFrame> frame,
                           int64 query_id,
                           const CefString& request,
                           bool persistent,
                           CefRefPtr<Callback> callback) {
        return false;
      }
    
      ///
      // Executed when a query has been canceled either explicitly using the
      // JavaScript cancel function or implicitly due to browser destruction,
      // navigation or renderer process termination. It will only be called for
      // the single handler that returned true from OnQuery for the same
      // |query_id|. No references to the associated Callback object should be
      // kept after this method is called, nor should any Callback methods be
      // executed.
      ///
      virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
                                   CefRefPtr<CefFrame> frame,
                                   int64 query_id) {}
    };
  • 自定义实现

    基于CEF的应用程序提供异步JaveScript绑定的自定义实现。简单的实现步骤如下:

    1. 在渲染进程中通过回调函数绑定JavaScript

      // In JavaScript register the callback function.
      app.setMessageCallback(‘binding_test‘, function(name, args) {
        document.getElementById(‘result‘).value = "Response: "+args[0];
      });
    2. 在渲染进程中维持回调函数中引用
      
      // Map of message callbacks.
      typedef std::map<std::pair<std::string, int>,
                       std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > >
                       CallbackMap;
      CallbackMap callback_map_;
      
      // In the CefV8Handler::Execute implementation for “setMessageCallback”.
      if (arguments.size() == 2 && arguments[0]->IsString() &&
          arguments[1]->IsFunction()) {
        std::string message_name = arguments[0]->GetStringValue();
        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
        int browser_id = context->GetBrowser()->GetIdentifier();
        callback_map_.insert(
            std::make_pair(std::make_pair(message_name, browser_id),
                           std::make_pair(context, arguments[1])));
      }
    3. 渲染过程向浏览器进程发送异步消息IPC过程要求执行工作。
    4. 浏览器进程接受IPC消息并执行工作。
    5. 浏览器完成工作后向渲染进程发送异步IPC消息返回结果
    6. 渲染进程接受到IPC消息并执行回调函数处理结果
      // Execute the registered JavaScript callback if any.
      if (!callback_map_.empty()) {
        const CefString& message_name = message->GetName();
        CallbackMap::const_iterator it = callback_map_.find(
            std::make_pair(message_name.ToString(),
                           browser->GetIdentifier()));
        if (it != callback_map_.end()) {
          // Keep a local reference to the objects. The callback may remove itself
          // from the callback map.
          CefRefPtr<CefV8Context> context = it->second.first;
          CefRefPtr<CefV8Value> callback = it->second.second;
      
          // Enter the context.
          context->Enter();
      
          CefV8ValueList arguments;
      
          // First argument is the message name.
          arguments.push_back(CefV8Value::CreateString(message_name));
      
          // Second argument is the list of message arguments.
          CefRefPtr<CefListValue> list = message->GetArgumentList();
          CefRefPtr<CefV8Value> args = CefV8Value::CreateArray(list->GetSize());
          SetList(list, args);  // Helper function to convert CefListValue to CefV8Value.
          arguments.push_back(args);
      
          // Execute the callback.
          CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(NULL, arguments);
          if (retval.get()) {
            if (retval->IsBool())
              handled = retval->GetBoolValue();
          }
      
          // Exit the context.
          context->Exit();
        }
      }
    7. 在CefRenderProcessHandler::OnContextReleased()函数中释放任何V8所关联的Context

五、同步请求

在浏览进程和渲染进程中很少使用同步通信。无论什么时候应该尽可能避免使用,因为会在渲染进程中影响性能。如里实在需要同步通信可考虑使用XMLHttpRequests。

六、网络层

CEF3默认网络请求处理的方式对主机应用程序是透明的,应用程序靠近CEF3网络层的关系会更多的暴露与网络相关的功能。在不同的线程上会引用不同的网络调用。所以一定要注意文档,妥善保护您的数据成员。

七、自定义请求

在浏览进程窗口中通过CefFrame::LoadURL()函数简单的加载URL,如:browser->GetMainFrame()->LoadURL(some_url);

应用程序希望发送更多复杂的请求包含自定义请求头或者使用CefFrame::LoadRequest()函数下载数据,这个方法接受CefRequest对象作为单一参数。

// Create a CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();

// Set the request URL.
request->SetURL(some_url);

// Set the request method. Supported methods include GET, POST, HEAD, DELETE and PUT.
request->SetMethod(“POST”);

// Optionally specify custom headers.
CefRequest::HeaderMap headerMap;
headerMap.insert(
    std::make_pair("X-My-Header", "My Header Value"));
request->SetHeaderMap(headerMap);

// Optionally specify upload content.
// The default “Content-Type” header value is "application/x-www-form-urlencoded".
// Set “Content-Type” via the HeaderMap if a different value is desired.
const std::string& upload_data = “arg1=val1&arg2=val2”;
CefRefPtr<CefPostData> postData = CefPostData::Create();
CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
element->SetToBytes(upload_data.size(), upload_data.c_str());
postData->AddElement(element);
request->SetPostData(postData);
八、独立于浏览器的请求

应用程序可以通过CefURLRequest类发送不与某个浏览器相关联的网络请求。实现CefURLRequestClient接口以处理响应结果。
CefURLRequest可在浏览进程和渲染进程中使用。

class MyRequestClient : public CefURLRequestClient {
 public:
  MyRequestClient()
    : upload_total_(0),
      download_total_(0) {}

  virtual void OnRequestComplete(CefRefPtr<CefURLRequest> request) OVERRIDE {
    CefURLRequest::Status status = request->GetRequestStatus();
    CefURLRequest::ErrorCode error_code = request->GetRequestError();
    CefRefPtr<CefResponse> response = request->GetResponse();

    // Do something with the response...
  }

  virtual void OnUploadProgress(CefRefPtr<CefURLRequest> request,
                                uint64 current,
                                uint64 total) OVERRIDE {
    upload_total_ = total;
  }

  virtual void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
                                  uint64 current,
                                  uint64 total) OVERRIDE {
    download_total_ = total;
  }

  virtual void OnDownloadData(CefRefPtr<CefURLRequest> request,
                              const void* data,
                              size_t data_length) OVERRIDE {
    download_data_ += std::string(static_cast<const char*>(data), data_length);
  }

 private:
  uint64 upload_total_;
  uint64 download_total_;
  std::string download_data_;

 private:
  IMPLEMENT_REFCOUNTING(MyRequestClient);
};
发送请求:
// Set up the CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();
// Populate |request| as shown above...

// Create the client instance.
CefRefPtr<MyRequestClient> client = new MyRequestClient();

// Start the request. MyRequestClient callbacks will be executed asynchronously.
CefRefPtr<CefURLRequest> url_request = CefURLRequest::Create(request, client.get());
// To cancel the request: url_request->Cancel();

CefURLRequest制定的请求还可以通过CefRequest::SetFlags()函数指定自定义行为:
UR_FLAG_SKIP_CACHE  当处理请求时,如果设置了缓存就跳过。
UR_FLAG_ALLOW_CACHED_CREDENTIALS  如果设置cookie可以发送请求和保存从响应。此必须被设置
UR_FLAG_REPORT_UPLOAD_PROGRESS 如果设置上载过程事件时将产生当请求体时。
UR_FLAG_REPORT_LOAD_TIMING 如里设置加载时间信息在请求时会被收集。
UR_FLAG_REPORT_RAW_HEADERS 如果设置头信息发送和接收的请求将被记录下来。 
UR_FLAG_NO_DOWNLOAD_DATA 如里设置了,CefURLRequestClient::OnDownloadData方法不会被调用。
UR_FLAG_NO_RETRY_ON_5XX 如果设置5 xx重定向错误将被传递到观察者,而不是自动重试。这个目前仅适用于来自浏览器的请求过程。 
如:request->SetFlags(UR_FLAG_SKIP_CACHE | UR_FLAG_NO_DOWNLOAD_DATA);

九、请求处理

在应用程序中CEF3支持两种方法来处理网络请求。计划处理程序方法允许注册的处理程序请求针对一个特定的起源(方案+域),请求拦截方法允许任意请求的处理在应用程序自由裁量权。使用HTTP方案而不是定制的方案,以避免一系列潜在问题。

如果选择使用自定义方案,你必须向CEF注册,如果想自定义方案有HTTP相同的行为,那么应该想标准方案一样注册。如果打算执行跨域请求其他方案或通过XMLHttpRequst发送POST请求到自定义方案中处理,那么应用使用HTTP方案来代替自定义方案以避免潜在的问题。哪里希望自定义属性通过cefApp::OnRegisterCustomSchemes()回调函数注册并在所有进程中实现。

void MyApp::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar)

{ // Register "client" as a standard scheme.

registrar->AddCustomScheme("client", true, false, false);

}

十、Scheme Handler

Scheme Handler通过CefRegisterSchemeHandlerFactory()函数注册,CefBrowserProcessHandler::OnContextInitialized()是此函数的最好调用的地方。

CefRegisterSchemeHandlerFactory("client", “myapp”, new MySchemeHandlerFactory());

Handler是用在内置方案和自定义方案。当使用内置方案时选择一个域名唯一标识应用程序。实现CefSchemeHandlerFactory和CefResourceHandler类处理请求和提供响应数据。如果使用自定义方案不要忘记实现CefApp::OnRegisterCustomSchemes函数。

十一、请求拦截

CefRequestHandler::GetResourceHandler()函数支持任意请求的拦截。使用与CefResourceHandler同样的类处理方法。如果使用自定义方案不要忘记使用 CefApp::OnRegisterCustomSchemes函数。

十二、Other Callbacks

CefRequestHandler接口处理各种各样的与网络相关的事件,包括认证、cookie处理、扩展协议、证书错误处理等等

时间: 2024-10-10 04:31:58

CEF3研究(三)的相关文章

JAVA 虚拟机深入研究(三)——Java内存区域

JAVA 虚拟机深入研究(一)--关于Java的一些历史 JAVA 虚拟机深入研究(二)--JVM虚拟机发展以及一些Java的新东西 JAVA 虚拟机深入研究(三)--Java内存区域 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的围城,城外的人想进去,城里的人想出来. Java运行的时候会把内存分为若干个,他们各有各的用途,每块区域的创建和销毁都是相对独立的,有的跟虚拟机一起混,有的则抱着用户的大腿同生共死. 按照第七版的<Java虚拟机规范>规定,JVM所管理的内存包括以下

Java面试准备之JVM详细研究三(类加载机制)

类加载过程 一个类从编写完成后,编译为字节码之后,它要装载进内存有七个阶段: 加载 => (验证-> 准备-> 解析)=> 初始化=> 使用=> 卸载 括号中的三个步骤可以整合成为 “连接”步骤.其中的步骤并不是一个阶段结束,一个阶段才开始的.只是说他们的开始阶段基本遵循此顺序(解析阶段更是可能在使用的时候才发生,目的是配合动态绑定),这些阶段都是互相交叉的混合式进行的,通常会在一个阶段执行过程中调用或激活另一个阶段. 1.加载 ”加载“的过程是”类加载“过程的一个阶段

王爽-汇编语言-综合研究三-使用内存空间

(一) 研究概述 数据不仅可以存储在寄存器中,还可以存储在内存中.这次我们就研究在C语言中,怎样直接在内存中存储数据.以及这样做的一些延伸问题.另外,在附录研究中,我们还探究了C语言中循环和分支结构的实现. (二) 研究过程 1) 直接在C语言中使用内存空间 此处援引书中的话: 对于存储空间来说,要使用他们一般都需要给出两个信息:一是指明存储空间所在.是哪个的信息:二是指明存储空间有多大的类型信息. 对于寄存器来说,就需要给出寄存器的名称,寄存器的名称就也包含了他们的类型信息. 对于内存空间来说

CEF3研究(四)之javascript集成

一.介绍 谷歌浏览器和CEF使用V8JavaScript Engine作为内容的JavaScript实现.在浏览器中的每个窗口都有它自己在的JS上下文提供作用域和在窗口中安全的执行JS代码.CEF暴露大量JS功能集成在客户端应用程序.CEF3的Webkit和JS在单独的渲染进程中运行.在渲染进程的主线程中使用TID_RENDERER 作为唯一标识.所有V8的执行必须放置在这个线程中.与JS执行相关的回调函数被暴露是通过CefRenderProcessHandler接口实现.当一个新的渲染进程被初

CEF3研究(二)

应用程序结构 每个CEF3应用程序都有一个相同的结构: 提供一个入口函数以初始化CEF和运行每个子进程逻辑和CEF消息处理 提供一个CefApp子类处理某个进程的回调 提供一个CefClinet子类处理某个浏览进程的回调 调用CefBrowserHost::CreateBrowser()函数创建浏览进程实例并使用CefLifeSpanHandler来管理浏览生命周期 2. 入口函数 CEF3应用程序都是以多进程方式运行的.这些进程都可以使用相同的可执行文件和独立的可执行文件被分配在每个子进程上.

数据结构与算法系列研究三——字符串

字符串的研究和KMP算法分析和实现 一.串的定义 串是计算机非数值处理的基本对象.串是一种特殊的线性表,它的每个结点仅由一个字符组成,并且单个元素是无意义的.    1.串(string):是由0个或多个字符组成的有限序列,记作:          S="a1a2...an"  (n>=0)          其中:S是串名,两个双引号括起来的字符序列为串的值.双引号不属于串.                   ai(1<=i<=n)为字母.数字或其它符号.    

C++的开源跨平台日志库glog学习研究(三)--杂项

在前面对glog分别做了两次学习,请看C++的开源跨平台日志库glog学习研究(一).C++的开源跨平台日志库glog学习研究(二)--宏的使用,这篇再做个扫尾工作,算是基本完成了. 编译期断言 动态断言在调试过程中是一个很重要的手段,而且我们使用的也比较多.相应的,静态断言,也即是编译期断言随着模板编程.元编程的发展,也表现出了动态断言所没有的优势:在编译期完成断言检查,而不是等到运行时! 比如在glog的源码中,有如下代码(logging.h line 908): 1 template <b

CEF3研究(一)

一.基本概览 C++ WrapperC++Wrapper(包装类)就是将C结构包装C++类. 这是C/C++API转换层通过translator tool自动产生的. 进程 CEF3用多进程运行. 主(浏览)进程 --处理窗口创建,绘制和网络访问,会产生相同的进程作为主应用程序,及处理主应用程序的逻辑. 渲染进程--处理闪烁渲染和JavaScript的执行,也处理一些应用程序逻辑,如JavaScript绑定和DOM的访问. scheme + domain进程--默认的进程模型的渲染进程.每个进程

Java-WebSocket 项目的研究(三) WebSocketClient 类 详解

通过之前两篇文章 Java-WebSocket 项目的研究(一) Java-WebSocket类图描述 Java-WebSocket 项目的研究(二) 小试身手:客户端连接服务器并发送消息实例 的介绍我们大概了解到了整个项目的类结构,其中有一个重要的类:WebSocketClient,下面就让我们详细了解一下这个类 首先看一下我们之前的类图关于WebSocketClient的描述,可以看出: 1.继承自WebSocketAdapter 2.依赖于类WebSocketImpl(实际上关于WebSo