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_UINewCefRunnableMethod(object&MyObject::MyMethodparam1param2));
在UI線程中執行某個方法:CefPostTask(TID_IO, NewCefRunnableFunction(MyFunction, param1, param2));
如果主機應用程序需要獲取運行循環的引用,可以使用CefTaskRunner類。 
例如:獲取線程的任務運行者:CefRefPtr<CefTaskRunnertask_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-Typeheader value is "application/x-www-form-urlencoded". // Set Content-Typevia 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處理、擴展協議、證書錯誤處理等等


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM