轉載:https://www.cnblogs.com/sinceret/p/10417941.html
轉載:https://stackoverflow.com/questions/48811756/registering-custom-backend-scheme-is-not-working-in-cef
轉載:https://www.twblogs.net/a/5c308e03bd9eee35b3a4d59a/zh-cn
轉載:http://www.voidcn.com/article/p-ffanxazs-bpp.html
轉載:https://magpcss.org/ceforum/viewtopic.php?f=6&t=13266
轉載:https://github.com/gabyx/ExecutionGraph/tree/3ff8b340d3ae1c87976104f0bbbda4c048e7eb2b(Github上Demo)
轉載:https://blog.csdn.net/ming19951224/article/details/81807159(解決思路參考)
轉載:https://blog.csdn.net/cdx1170776994/article/details/76571535
轉載:https://blog.csdn.net/zhangpeng_linux/article/details/85858814
轉載:https://www.cnblogs.com/himax/p/how_to_intercept_response_in_cef_using_csharp.html
在谷歌瀏覽器中點擊設置,地址欄里出現的不是普通網址,而是chrome://settings/,這個地址就是谷歌瀏覽器的自定義scheme,cef也提供了自定義協議手段。
scheme就是一種協議,比如http://helloworld.com中的http就是scheme,不過它是內置的。client://helloworld.com中的client就是一個自定義scheme。
當javascript通過XMLHttpRequest請求地址client://helloworld.com時,我們c++中實現的處理器,就會收到這個請求。
XMLHttpRequest可以通過post方法將二進制數據如arraybuffer發送到browser進程。
1.當javascript需要傳遞大量二進制數據給browser進程的c++代碼時,可以使用POST方法,
2.當javascript需要從browser進程的c++代碼拿到大量二進制數據時,可以使用GET方法。
1.CEF注冊custom scheme 來接受http請求
在cef中我們可以通過以下接口來注冊scheme
bool CefRegisterSchemeHandlerFactory( const CefString& scheme_name, const CefString& domain_name, CefRefPtr<CefSchemeHandlerFactory> factory);
注冊一個自定義http scheme
CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());
這個scheme限定了,scheme為"http",domain為"test",只有當請求為"http://test/"開頭時,才會執行自定義c++代碼。
處理器工廠為MySchemeHandlerFactory,是我們自己實現的處理請求的c++代碼。
繼承一個工廠類MySchemeHandlerFactory : public CefSchemeHandlerFactory
需要包含#include "include/cef_scheme.h"
.h文件
#ifndef _MYSCHEMEHANDLERFACTORY_H_ #define _MYSCHEMEHANDLERFACTORY_H_ #include "include\cef_scheme.h" class MySchemeHandlerFactory : public CefSchemeHandlerFactory { public: virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& scheme_name, CefRefPtr<CefRequest> request) OVERRIDE;// Return a new resource handler instance to handle the request. private: IMPLEMENT_REFCOUNTING(MySchemeHandlerFactory); }; #endif //_MYSCHEMEHANDLERFACTORY_H_
.cpp文件
#include "MySchemeHandlerFactory.h" #include "MyResourceHandler.h" #include "include/cef_parser.h" #include "include/wrapper/cef_helpers.h" #include "include/wrapper/cef_stream_resource_handler.h" #include <shlwapi.h> #pragma comment(lib, "shlwapi.lib") CefRefPtr<CefResourceHandler> MySchemeHandlerFactory::Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& scheme_name, CefRefPtr<CefRequest> request) { CEF_REQUIRE_IO_THREAD(); CefString url = request->GetURL(); cef_uri_unescape_rule_t unescape_rule = UU_SPACES;//處理路徑中有空格 CefString& decoded_value = CefURIDecode(url, true, unescape_rule); CefURLParts urlParts; if (CefParseURL(url, urlParts)) { std::wstring filePath = CefURIDecode(CefString(&urlParts.query),true,unescape_rule);//處理有中文路徑 CefRefPtr<CefStreamReader> fileStream = CefStreamReader::CreateForFile(CefString(filePath)); if (fileStream != nullptr) { // "ext" 獲取擴展名 例如:"txt"、"jpg" std::wstring ext; ext = PathFindExtension(filePath.c_str()); ext =ext.substr(1, ext.length()); CefString mimeType(CefGetMimeType(ext)); //todo: Complete known mime times with web-font extensions if (mimeType.empty()) { //mimeType = "font/" + fileExtension; } return CefRefPtr<CefStreamResourceHandler>(new CefStreamResourceHandler(mimeType, fileStream));//以二進制流的方式把文件返給js並顯示到html標簽中 } } return new MyResourceHandler(); }
2.繼承一個資源類class MyResourceHandler : public CefResourceHandler
ProcessRequest 處理請求,允許請求返回true;取消請求返回false。當需要返回的數據就緒后(自行請求網絡或者文件就緒)或立即調用callback.Continue(),通知cef進入下一步:GetResponseHeaders。
GetResponseHeaders 設置響應頭,在這里可以設置mime-type,響應長度,其它頭等。不確定長度設置響應長度為-1。
ReadResponse 設置響應結果。可以設置響應具體內容,設置已讀取長度。cef調用完上一步后會繼續調用此方法。根據響應長度和數據就緒情況,調用此方法的次數和策略不同。
當響應長度為-1時,cef無法根據已讀長度確定是否已讀取完畢,必需根據返回值false來結束讀取。
當響應長度大於0時,cef根據每次調用得到的已讀長度,或返回值false來結束讀取。
如果數據未就緒,可以設置讀取長度為0 ,返回true,並在稍后調用callbak.Continue()通知cef調用此方法讀取響應內容。
注意:
當響應長度為-1時,必需保證此方法至少執行兩次,第一次返回true表示數據全部就緒,第二次返回false表示讀取結束。
當響應長度大於0時,設置內容和已讀長度,返回true。則此方法只執行一次。
若實際返回的響應內容,長度大於之前設置的響應總長度,則返回內容將被截取。
#ifndef _MYRESOURCEHANDLER_H_ #define _MYRESOURCEHANDLER_H_ #include <fstream> #include <stdio.h> #include "include\cef_resource_handler.h" class MyResourceHandler : public CefResourceHandler { public: MyResourceHandler(); virtual bool ProcessRequest(CefRefPtr<CefRequest> request, CefRefPtr<CefCallback> callback)OVERRIDE; virtual void GetResponseHeaders(CefRefPtr<CefResponse> response, int64& response_length, CefString& redirectUrl) OVERRIDE; virtual void Cancel() OVERRIDE{ // Cancel the response... } virtual bool ReadResponse(void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr<CefCallback> callback) OVERRIDE; virtual bool CanGetCookie(const CefCookie& cookie); virtual bool CanSetCookie(const CefCookie& cookie); private: std::string data_; IMPLEMENT_REFCOUNTING(MyResourceHandler); }; #endif//_MYRESOURCEHANDLER_H_
.cpp文件
#include "MyResourceHandler.h" MyResourceHandler::MyResourceHandler() { } bool MyResourceHandler::ProcessRequest(CefRefPtr<CefRequest> request, CefRefPtr<CefCallback> callback) { std::string url = request->GetURL(); data_ = "hello cef";//返回到頁面中的內容 callback->Continue();//這個一定要有 return true;// } void MyResourceHandler::GetResponseHeaders(CefRefPtr<CefResponse> response, int64& response_length, CefString& redirectUrl) { response->SetMimeType("text/plain"); response->SetStatus(200); response_length = data_.length(); } bool MyResourceHandler::ReadResponse(void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr<CefCallback> callback) { int size = static_cast<int>(data_.length()); memcpy(data_out, data_.c_str(), size); bytes_read = size; return true; } bool MyResourceHandler::CanGetCookie(const CefCookie& cookie) { return false; } bool MyResourceHandler::CanSetCookie(const CefCookie& cookie) { return false; }
3.在初始化cef那幾行代碼后面增加一句
CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());
CefSettings cSettings; const char* loc = "zh-CN"; cSettings.no_sandbox = true; cSettings.multi_threaded_message_loop = true; //cSettings.single_process = false; cSettings.log_severity = LOGSEVERITY_DISABLE;//設置日志級別,解決安裝啟動佰卓數安后桌面出現一個debug.log文件(調試階段可以去掉) CefString(&cSettings.locale).FromASCII(loc); cef_string_from_ascii(loc, strlen(loc), &cSettings.locale); // Execute the secondary process, if any. CefInitialize(main_args, cSettings, spApp.get(), sandbox_info); /***************************************結束初始化cef*******************************************/ //自定義scheme CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());
4.CEF瀏覽器加載的靜態html
<html> <script> function do_post() { //alert('posting'); var xhr = new XMLHttpRequest(); var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\梅西.jpg"; xhr.open("GET", zurl, true); xhr.responseType = 'blob'; xhr.onload = function() { //console.log(this); if (this.status == 200) { var blob = this.response; var img = document.createElement("img"); img.onload = function(e) { window.URL.revokeObjectURL(img.src); }; //alert(blob); img.src = window.URL.createObjectURL(blob); document.body.appendChild(img); } } xhr.send(); //alert('posted'); } function do_video() { //alert('posting'); var xhr = new XMLHttpRequest(); var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\稻香.mp4"; xhr.open("GET", zurl, true); xhr.responseType = 'blob'; xhr.onload = function() { //console.log(this); if (this.status == 200) { var blob = this.response; var video = document.querySelector("video"); //alert(window.URL.createObjectURL(blob)); video.src = window.URL.createObjectURL(blob); } } xhr.send(); //alert('posted'); } function do_audio() { //alert('posting'); var xhr = new XMLHttpRequest(); var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\千年等一回.mp3"; xhr.open("GET", zurl, true); xhr.responseType = 'blob'; xhr.onload = function() { //console.log(this); if (this.status == 200) { var blob = this.response; var audio = document.querySelector("audio"); //alert(window.URL.createObjectURL(blob)); audio.src = window.URL.createObjectURL(blob); } } xhr.send(); //alert('posted'); } </script> <body onLoad=""> <div> <!-- <img src="圖片路徑、地址" alt="" /> --> <input type="button" value="加載圖片" onclick="do_post()"> <input type="button" value="加載視頻" onclick="do_video();"> <input type="button" value="加載音頻" onclick="do_audio();"> </div> <video controls="true" ></video> <audio controls="true" ></audio> <p>Hello everyone! </p> </body> </html>