renderer进程中的JS给CEF的browserC++进程发送消息
1.1 renderer进程中的JS给CEF的browserC++进程发送消息
1.1.1 流程原理介绍
renderer进程中暴露一个query函数和cancel函数,在renderer进程运行的JavaScript代码调用window.cefQuery,cefQueryCancel函数将消息发给browser进程的c++代码处理消息,.cefQuery,cefQueryCancel是在C++代码中的CefMessageRouterConfig对象中注册的,如果你希望注册不同名字的函数,需要在CefMessageRouterConfig对象中设置,如果你要 设置两套注册函数,router也要两套。rendder进程和browser进程之间是通两端的消息路由传递消息的,render进程消息路由CefMessageRouterRendererSide,browser进程消息路由CefMessageRouterBrowserSide。在renderer进程的OnProcessMessageRecieved, OnContextCreated OnContextReleased函数中通过router对象调用CefMessageRouterRendererSide中同名的函数(OnContextCreated注册JavaScript的函数;OnContextReleased取消与上下文相关的正在进行中的query,调用OnQueryCanceled会被调用;OnProcessMessageRecieved消被router正确处理返回true),消息路由会将从renderer进程发送给browser进程中的CefMessageRouterBrowserSide路由。renderer进程支持一般的JavaScript回调函数注册和执行,browser进程中的路由router可以对消息采用一个或者多个不同逻辑的处理实例来处理。前端JavaScript调用window.cefQuery函数,renderder通过消息路由将消息发给browser进程,browser进程的一个或者多个queryhander就会接收到消息,每个handler可以选择处理或者忽略消息。如果处理了消息,如果响应有效,需要执行回调函数Callback::Success,如果出错,需要调用Callback::Failure。这两个函数会同步调用renderer进程中JavaScript的回调函数onSuccess和onFailure,返回执行结果。如果query没有被borwser进程的hander处理,则会自动取消,JavaScript的onFaillure回调函数会被执行,错误码-1;qurry可以是持续的或者非持续的
(1)如果是持续的,需要在browser进程中定义map<int64,CefRefPtr<callback>>保存持续的请求queryid和callback对象,在onquery函数中保存进入map,当JavaScript调用window.CefQueryCancel函数,或者C++代码中使用了CallFailure函数,或者上下文context销毁时,需要将query在C++的OnQueryCancel回调函数中从map中删除。
(2)如果query是非持续的,注册会在JavaScript的回调函数执行后自动清除。不用保存到map主要实现在CEF的cef_message_router.h文件中。
1.1.2 query请模式
(1) 一次请求模式,使用非持续query,handle执行请求,返回响应, query销毁。
(2) 广播模式。使用持久性query注册为一个广播接受者,handler一直追踪所有的注册回调,并执行他们来发送广播消息。
(3) 订阅模式。使用持续性query注册为JavaScript的订阅接受者,handler在第一次请求时初始化订阅feed,将响应发送给所有的订阅者。没有一个注册的JavaScript接收者时,handle会取消订阅feed。
render进程中一般只要一个router就可以了,如果需要注册不同的JavaScript函数名字,需要多个router。当browser进程需要多个hadler来处理不同的消息,这样做是非常有用的。
1.1.3 消息通讯具体开发步骤
(1) 在renderer进程的rendererApp类的构造函数中创建CefMessageRouterRendererSide对象。
CefMessageRouterConfig config;
m_messagerouter=CefMessageRouterRendererSide.Create(config);
(2) 在renderer进程的OnProcessMessageRecieved, OnContextReleased OnContextCreated函数中通过router对象调用CefMessageRouterRendererSide中同名的函数,消息就会发送给browser进程中的CefMessageRouterBrowserSide路由。
void RendererApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
m_messageRouter->OnContextCreated(browser, frame, context);
}
void RendererApp::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
m_messageRouter->OnContextReleased(browser, frame, context);
}
bool RendererApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
return m_messageRouter->OnProcessMessageReceived(
browser, source_process, message);
}
(3) 定义处理类QueryHand,继承类 CefMessageRouterBrowserSide ::Handler类,实现OnQuery函数。
#pragma once
#include <map>
#include "include/wrapper/cef_message_router.h"
#include "FrameCommunication/CefInterfaceCallback.h"
class CefQueryHandler : public CefBaseRefCounted
, public CefMessageRouterBrowserSide::Handler
{
protected:
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback);
virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id);
void complete(int64 query_id, int error_code, const CefString response);
private:
std::map<int64, CefRefPtr<Callback>> m_pendings;
IMPLEMENT_REFCOUNTING(CefQueryHandler);
};
(4)在browser进程中clienthand类中创建对象CefMessageRouterConfig
CefMessageRouterConfig用来定义js调用的函数名称,下面给出的是默认函数名称。
CefMessageRouterConfig config;
// config.js_query_function = "cefQuery";
// config.js_cancel_function = "cefQueryCancel";
(5)在browser进程中根据配置创建CefMessageRouterBrowserSide对象管理QueryHand。创建QueryHand对象,并添加到CefMessageRouterBrowserSide。clienthandler的OnBeforeClose、OnBeforeBrowse、OnProcessMessageReceived函数中执行CefMessageRouterBrowserSide对象中对应名称的函数(在OnBeforeClose、OnBeforeBrowse中调用,这样做的目的是网页刷新或者关闭时,所有正在进行中的query会取消掉,OnQueryCanceled函数会被调用,取消所有正在进行的query。在OnProcessMessageReceived中调用,则是消息如果被router正确处理,则返回true,否则返回false)。定义CefMessageRouterBrowserSide对象用来管理QueryHand对象。
m_queryHand=new QueryHand();
m_browser_side_router= CefMessageRouterBrowserSide::Create(config);
m_browser_side_router ->AddHandler(my_handler.);
bool ClientHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) {
m_messageRouter->OnBeforeBrowse(browser, frame);
}
bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
if (m_messageRouter->OnProcessMessageReceived(browser, source_process,
message)) {
return true;
}
(6)JavaScript调用cefQuery函数
<html>
<head>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
$(document).dblclick(function(){
//alert("get doubleclicked");
if(window==top.window){//判断是否是嵌入
window.cefQuery({发给CEF消息
request: "message-content",
persistent: false,
onSuccess: function(response) { alert(response); },
onFailure: function(code, msg) { alert(code + " - " + msg); }
});
}
});
});
</script>
</head>
<body>
</body>
</html>
(7)onquery函数接收消息,处理消息,执行回调将queryid和回调对象保存到map,OnQueryCanceled函数中从map中找到对应的queryid,调用callback对象的onfailure函数,然后从map中清除。
void MyHandler::OnQuery(int64 query_id,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) {
// 处理消息request
if (persistent)
{
bRet = m_pendings.insert(std::make_pair(query_id, callback)).second;
}
else
{
callback->Success("success");
bRet = true;
}
}
void CefQueryHandler::OnQueryCanceled(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int64 query_id)
{
auto it = m_pendings.find(query_id);
if (it != m_pendings.end())
{
it->second->Failure(-1,"canceled");
m_pendings.erase(it);
}
}
Cef消息路由类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | // Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the name Chromium Embedded // Framework nor the names of its contributors may be used to endorse // or promote products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // --------------------------------------------------------------------------- // // The contents of this file are only available to applications that link // against the libcef_dll_wrapper target. // #ifndef CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ #define CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ #pragma once #include "include/base/cef_ref_counted.h" #include "include/cef_base.h" #include "include/cef_browser.h" #include "include/cef_process_message.h" #include "include/cef_v8.h" // The below classes implement support for routing aynchronous messages between // JavaScript running in the renderer process and C++ running in the browser // process. An application interacts with the router by passing it data from // standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved, // OnContextCreated, etc). The renderer-side router supports generic JavaScript // callback registration and execution while the browser-side router supports // application-specific logic via one or more application-provided Handler // instances. // // The renderer-side router implementation exposes a query function and a cancel // function via the JavaScript 'window' object: // // // 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); // // When |window.cefQuery| is executed the request is sent asynchronously to one // or more C++ Handler objects registered in the browser process. Each C++ // Handler can choose to either handle or ignore the query in the // Handler::OnQuery callback. If a Handler chooses to handle the query then it // should execute Callback::Success when a response is available or // Callback::Failure if an error occurs. This will result in asynchronous // execution of the associated JavaScript callback in the renderer process. Any // queries unhandled by C++ code in the browser process will be automatically // canceled and the associated JavaScript onFailure callback will be executed // with an error code of -1. // // Queries can be either persistent or non-persistent. If the query is // persistent than the callbacks will remain registered until one of the // following conditions are met: // // A. The query is canceled in JavaScript using the |window.cefQueryCancel| // function. // B. The query is canceled in C++ code using the Callback::Failure function. // C. The context associated with the query is released due to browser // destruction, navigation or renderer process termination. // // If the query is non-persistent then the registration will be removed after // the JavaScript callback is executed a single time. If a query is canceled for // a reason other than Callback::Failure being executed then the associated // Handler's OnQueryCanceled method will be called. // // Some possible usage patterns include: // // One-time Request. Use a non-persistent query to send a JavaScript request. // The Handler evaluates the request and returns the response. The query is // then discarded. // // Broadcast. Use a persistent query to register as a JavaScript broadcast // receiver. The Handler keeps track of all registered Callbacks and executes // them sequentially to deliver the broadcast message. // // Subscription. Use a persistent query to register as a JavaScript subscription // receiver. The Handler initiates the subscription feed on the first request // and delivers responses to all registered subscribers as they become // available. The Handler cancels the subscription feed when there are no // longer any registered JavaScript receivers. // // Message routing occurs on a per-browser and per-context basis. Consequently, // additional application logic can be applied by restricting which browser or // context instances are passed into the router. If you choose to use this // approach do so cautiously. In order for the router to function correctly any // browser or context instance passed into a single router callback must then // be passed into all router callbacks. // // There is generally no need to have multiple renderer-side routers unless you // wish to have multiple bindings with different JavaScript function names. It // can be useful to have multiple browser-side routers with different client- // provided Handler instances when implementing different behaviors on a per- // browser basis. // // This implementation places no formatting restrictions on payload content. // An application may choose to exchange anything from simple formatted // strings to serialized XML or JSON data. // // // EXAMPLE USAGE // // 1. Define the router configuration. You can optionally specify settings // like the JavaScript function names. The configuration must be the same in // both the browser and renderer processes. If using multiple routers in the // same application make sure to specify unique function names for each // router configuration. // // // Example config object showing the default values. // CefMessageRouterConfig config; // config.js_query_function = "cefQuery"; // config.js_cancel_function = "cefQueryCancel"; // // 2. Create an instance of CefMessageRouterBrowserSide in the browser process. // You might choose to make it a member of your CefClient implementation, // for example. // // browser_side_router_ = CefMessageRouterBrowserSide::Create(config); // // 3. Register one or more Handlers. The Handler instances must either outlive // the router or be removed from the router before they're deleted. // // browser_side_router_->AddHandler(my_handler); // // 4. Call all required CefMessageRouterBrowserSide methods from other callbacks // in your CefClient implementation (OnBeforeClose, etc). See the // CefMessageRouterBrowserSide class documentation for the complete list of // methods. // // 5. Create an instance of CefMessageRouterRendererSide in the renderer // process. // You might choose to make it a member of your CefApp implementation, for // example. // // renderer_side_router_ = CefMessageRouterRendererSide::Create(config); // // 6. Call all required CefMessageRouterRendererSide methods from other // callbacks in your CefRenderProcessHandler implementation // (OnContextCreated, etc). See the CefMessageRouterRendererSide class // documentation for the complete list of methods. // // 7. Execute the query function from JavaScript code. // // window.cefQuery({request: 'my_request', // persistent: false, // onSuccess: function(response) { print(response); }, // onFailure: function(error_code, error_message) {} }); // // 8. Handle the query in your Handler::OnQuery implementation and execute the // appropriate callback either immediately or asynchronously. // // void MyHandler::OnQuery(int64 query_id, // CefRefPtr<CefBrowser> browser, // CefRefPtr<CefFrame> frame, // const CefString& request, // bool persistent, // CefRefPtr<Callback> callback) { // if (request == "my_request") { // callback->Continue("my_response"); // return true; // } // return false; // Not handled. // } // // 9. Notice that the onSuccess callback is executed in JavaScript. /// // Used to configure the query router. The same values must be passed to both // CefMessageRouterBrowserSide and CefMessageRouterRendererSide. If using // multiple router pairs make sure to choose values that do not conflict. /// struct CefMessageRouterConfig { CefMessageRouterConfig(); // Name of the JavaScript function that will be added to the 'window' object // for sending a query. The default value is "cefQuery". CefString js_query_function; // Name of the JavaScript function that will be added to the 'window' object // for canceling a pending query. The default value is "cefQueryCancel". CefString js_cancel_function; }; /// // Implements the browser side of query routing. The methods of this class may // be called on any browser process thread unless otherwise indicated. /// class CefMessageRouterBrowserSide : public base::RefCountedThreadSafe<CefMessageRouterBrowserSide> { public : /// // Callback associated with a single pending asynchronous query. Execute the // Success or Failure method to send an asynchronous response to the // associated JavaScript handler. It is a runtime error to destroy a Callback // object associated with an uncanceled query without first executing one of // the callback methods. The methods of this class may be called on any // browser process thread. /// class Callback : public CefBaseRefCounted { 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; }; /// // Implement this interface to handle queries. All methods will be executed on // the browser process UI thread. /// class Handler { public : typedef CefMessageRouterBrowserSide::Callback Callback; /// // 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) {} virtual ~Handler() {} }; /// // Create a new router with the specified configuration. /// static CefRefPtr<CefMessageRouterBrowserSide> Create( const CefMessageRouterConfig& config); /// // Add a new query handler. If |first| is true it will be added as the first // handler, otherwise it will be added as the last handler. Returns true if // the handler is added successfully or false if the handler has already been // added. Must be called on the browser process UI thread. The Handler object // must either outlive the router or be removed before deletion. /// virtual bool AddHandler(Handler* handler, bool first) = 0; /// // Remove an existing query handler. Any pending queries associated with the // handler will be canceled. Handler::OnQueryCanceled will be called and the // associated JavaScript onFailure callback will be executed with an error // code of -1. Returns true if the handler is removed successfully or false // if the handler is not found. Must be called on the browser process UI // thread. /// virtual bool RemoveHandler(Handler* handler) = 0; /// // Cancel all pending queries associated with either |browser| or |handler|. // If both |browser| and |handler| are NULL all pending queries will be // canceled. Handler::OnQueryCanceled will be called and the associated // JavaScript onFailure callback will be executed in all cases with an error // code of -1. /// virtual void CancelPending(CefRefPtr<CefBrowser> browser, Handler* handler) = 0; /// // Returns the number of queries currently pending for the specified |browser| // and/or |handler|. Either or both values may be empty. Must be called on the // browser process UI thread. /// virtual int GetPendingCount(CefRefPtr<CefBrowser> browser, Handler* handler) = 0; // The below methods should be called from other CEF handlers. They must be // called exactly as documented for the router to function correctly. /// // Call from CefLifeSpanHandler::OnBeforeClose. Any pending queries associated // with |browser| will be canceled and Handler::OnQueryCanceled will be // called. No JavaScript callbacks will be executed since this indicates // destruction of the browser. /// virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) = 0; /// // Call from CefRequestHandler::OnRenderProcessTerminated. Any pending queries // associated with |browser| will be canceled and Handler::OnQueryCanceled // will be called. No JavaScript callbacks will be executed since this // indicates destruction of the context. /// virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) = 0; /// // Call from CefRequestHandler::OnBeforeBrowse only if the navigation is // allowed to proceed. If |frame| is the main frame then any pending queries // associated with |browser| will be canceled and Handler::OnQueryCanceled // will be called. No JavaScript callbacks will be executed since this // indicates destruction of the context. /// virtual void OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) = 0; /// // Call from CefClient::OnProcessMessageReceived. Returns true if the message // is handled by this router or false otherwise. /// virtual bool OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) = 0; protected : // Protect against accidental deletion of this object. friend class base::RefCountedThreadSafe<CefMessageRouterBrowserSide>; virtual ~CefMessageRouterBrowserSide() {} }; /// // Implements the renderer side of query routing. The methods of this class must // be called on the render process main thread. /// class CefMessageRouterRendererSide : public base::RefCountedThreadSafe<CefMessageRouterRendererSide> { public : /// // Create a new router with the specified configuration. /// static CefRefPtr<CefMessageRouterRendererSide> Create( const CefMessageRouterConfig& config); /// // Returns the number of queries currently pending for the specified |browser| // and/or |context|. Either or both values may be empty. /// virtual int GetPendingCount(CefRefPtr<CefBrowser> browser, CefRefPtr<CefV8Context> context) = 0; // The below methods should be called from other CEF handlers. They must be // called exactly as documented for the router to function correctly. /// // Call from CefRenderProcessHandler::OnContextCreated. Registers the // JavaScripts functions with the new context. /// virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) = 0; /// // Call from CefRenderProcessHandler::OnContextReleased. Any pending queries // associated with the released context will be canceled and // Handler::OnQueryCanceled will be called in the browser process. /// virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) = 0; /// // Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true // if the message is handled by this router or false otherwise. /// virtual bool OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) = 0; protected : // Protect against accidental deletion of this object. friend class base::RefCountedThreadSafe<CefMessageRouterRendererSide>; virtual ~CefMessageRouterRendererSide() {} }; #endif // CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ |
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix