1 // Copyright (c) 2014 Marshall A. Greenblatt. All rights reserved. 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google Inc. nor the name Chromium Embedded 14 // Framework nor the names of its contributors may be used to endorse 15 // or promote products derived from this software without specific prior 16 // written permission. 17 // 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 // 30 // --------------------------------------------------------------------------- 31 // 32 // The contents of this file are only available to applications that link 33 // against the libcef_dll_wrapper target. 34 // 35 36 #ifndef CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ 37 #define CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ 38 #pragma once 39 40 #include "include/base/cef_ref_counted.h" 41 #include "include/cef_base.h" 42 #include "include/cef_browser.h" 43 #include "include/cef_process_message.h" 44 #include "include/cef_v8.h" 45 46 // The below classes implement support for routing aynchronous messages between 47 // JavaScript running in the renderer process and C++ running in the browser 48 // process. An application interacts with the router by passing it data from 49 // standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageReceived, 50 // OnContextCreated, etc). The renderer-side router supports generic JavaScript 51 // callback registration and execution while the browser-side router supports 52 // application-specific logic via one or more application-provided Handler 53 // instances. 54 // 55 // The renderer-side router implementation exposes a query function and a cancel 56 // function via the JavaScript 'window' object: 57 // 58 // // Create and send a new query. 59 // var request_id = window.cefQuery({ 60 // request: 'my_request', 61 // persistent: false, 62 // onSuccess: function(response) {}, 63 // onFailure: function(error_code, error_message) {} 64 // }); 65 // 66 // // Optionally cancel the query. 67 // window.cefQueryCancel(request_id); 68 // 69 // When |window.cefQuery| is executed the request is sent asynchronously to one 70 // or more C++ Handler objects registered in the browser process. Each C++ 71 // Handler can choose to either handle or ignore the query in the 72 // Handler::OnQuery callback. If a Handler chooses to handle the query then it 73 // should execute Callback::Success when a response is available or 74 // Callback::Failure if an error occurs. This will result in asynchronous 75 // execution of the associated JavaScript callback in the renderer process. Any 76 // queries unhandled by C++ code in the browser process will be automatically 77 // canceled and the associated JavaScript onFailure callback will be executed 78 // with an error code of -1. 79 // 80 // Queries can be either persistent or non-persistent. If the query is 81 // persistent then the callbacks will remain registered until one of the 82 // following conditions are met: 83 // 84 // A. The query is canceled in JavaScript using the |window.cefQueryCancel| 85 // function. 86 // B. The query is canceled in C++ code using the Callback::Failure function. 87 // C. The context associated with the query is released due to browser 88 // destruction, navigation or renderer process termination. 89 // 90 // If the query is non-persistent then the registration will be removed after 91 // the JavaScript callback is executed a single time. If a query is canceled for 92 // a reason other than Callback::Failure being executed then the associated 93 // Handler's OnQueryCanceled method will be called. 94 // 95 // Some possible usage patterns include: 96 // 97 // One-time Request. Use a non-persistent query to send a JavaScript request. 98 // The Handler evaluates the request and returns the response. The query is 99 // then discarded. 100 // 101 // Broadcast. Use a persistent query to register as a JavaScript broadcast 102 // receiver. The Handler keeps track of all registered Callbacks and executes 103 // them sequentially to deliver the broadcast message. 104 // 105 // Subscription. Use a persistent query to register as a JavaScript subscription 106 // receiver. The Handler initiates the subscription feed on the first request 107 // and delivers responses to all registered subscribers as they become 108 // available. The Handler cancels the subscription feed when there are no 109 // longer any registered JavaScript receivers. 110 // 111 // Message routing occurs on a per-browser and per-context basis. Consequently, 112 // additional application logic can be applied by restricting which browser or 113 // context instances are passed into the router. If you choose to use this 114 // approach do so cautiously. In order for the router to function correctly any 115 // browser or context instance passed into a single router callback must then 116 // be passed into all router callbacks. 117 // 118 // There is generally no need to have multiple renderer-side routers unless you 119 // wish to have multiple bindings with different JavaScript function names. It 120 // can be useful to have multiple browser-side routers with different client- 121 // provided Handler instances when implementing different behaviors on a per- 122 // browser basis. 123 // 124 // This implementation places no formatting restrictions on payload content. 125 // An application may choose to exchange anything from simple formatted 126 // strings to serialized XML or JSON data. 127 // 128 // 129 // EXAMPLE USAGE 130 // 131 // 1. Define the router configuration. You can optionally specify settings 132 // like the JavaScript function names. The configuration must be the same in 133 // both the browser and renderer processes. If using multiple routers in the 134 // same application make sure to specify unique function names for each 135 // router configuration. 136 // 137 // // Example config object showing the default values. 138 // CefMessageRouterConfig config; 139 // config.js_query_function = "cefQuery"; 140 // config.js_cancel_function = "cefQueryCancel"; 141 // 142 // 2. Create an instance of CefMessageRouterBrowserSide in the browser process. 143 // You might choose to make it a member of your CefClient implementation, 144 // for example. 145 // 146 // browser_side_router_ = CefMessageRouterBrowserSide::Create(config); 147 // 148 // 3. Register one or more Handlers. The Handler instances must either outlive 149 // the router or be removed from the router before they're deleted. 150 // 151 // browser_side_router_->AddHandler(my_handler); 152 // 153 // 4. Call all required CefMessageRouterBrowserSide methods from other callbacks 154 // in your CefClient implementation (OnBeforeClose, etc). See the 155 // CefMessageRouterBrowserSide class documentation for the complete list of 156 // methods. 157 // 158 // 5. Create an instance of CefMessageRouterRendererSide in the renderer 159 // process. 160 // You might choose to make it a member of your CefApp implementation, for 161 // example. 162 // 163 // renderer_side_router_ = CefMessageRouterRendererSide::Create(config); 164 // 165 // 6. Call all required CefMessageRouterRendererSide methods from other 166 // callbacks in your CefRenderProcessHandler implementation 167 // (OnContextCreated, etc). See the CefMessageRouterRendererSide class 168 // documentation for the complete list of methods. 169 // 170 // 7. Execute the query function from JavaScript code. 171 // 172 // window.cefQuery({request: 'my_request', 173 // persistent: false, 174 // onSuccess: function(response) { print(response); }, 175 // onFailure: function(error_code, error_message) {} }); 176 // 177 // 8. Handle the query in your Handler::OnQuery implementation and execute the 178 // appropriate callback either immediately or asynchronously. 179 // 180 // void MyHandler::OnQuery(int64 query_id, 181 // CefRefPtr<CefBrowser> browser, 182 // CefRefPtr<CefFrame> frame, 183 // const CefString& request, 184 // bool persistent, 185 // CefRefPtr<Callback> callback) { 186 // if (request == "my_request") { 187 // callback->Continue("my_response"); 188 // return true; 189 // } 190 // return false; // Not handled. 191 // } 192 // 193 // 9. Notice that the onSuccess callback is executed in JavaScript. 194 195 /// 196 // Used to configure the query router. The same values must be passed to both 197 // CefMessageRouterBrowserSide and CefMessageRouterRendererSide. If using 198 // multiple router pairs make sure to choose values that do not conflict. 199 /// 200 struct CefMessageRouterConfig { 201 CefMessageRouterConfig(); 202 203 // Name of the JavaScript function that will be added to the 'window' object 204 // for sending a query. The default value is "cefQuery". 205 CefString js_query_function; 206 207 // Name of the JavaScript function that will be added to the 'window' object 208 // for canceling a pending query. The default value is "cefQueryCancel". 209 CefString js_cancel_function; 210 }; 211 212 /// 213 // Implements the browser side of query routing. The methods of this class may 214 // be called on any browser process thread unless otherwise indicated. 215 /// 216 class CefMessageRouterBrowserSide 217 : public base::RefCountedThreadSafe<CefMessageRouterBrowserSide> { 218 public: 219 /// 220 // Callback associated with a single pending asynchronous query. Execute the 221 // Success or Failure method to send an asynchronous response to the 222 // associated JavaScript handler. It is a runtime error to destroy a Callback 223 // object associated with an uncanceled query without first executing one of 224 // the callback methods. The methods of this class may be called on any 225 // browser process thread. 226 /// 227 class Callback : public CefBaseRefCounted { 228 public: 229 /// 230 // Notify the associated JavaScript onSuccess callback that the query has 231 // completed successfully with the specified |response|. 232 /// 233 virtual void Success(const CefString& response) = 0; 234 235 /// 236 // Notify the associated JavaScript onFailure callback that the query has 237 // failed with the specified |error_code| and |error_message|. 238 /// 239 virtual void Failure(int error_code, const CefString& error_message) = 0; 240 }; 241 242 /// 243 // Implement this interface to handle queries. All methods will be executed on 244 // the browser process UI thread. 245 /// 246 class Handler { 247 public: 248 using Callback = CefMessageRouterBrowserSide::Callback; 249 250 /// 251 // Executed when a new query is received. |query_id| uniquely identifies the 252 // query for the life span of the router. Return true to handle the query 253 // or false to propagate the query to other registered handlers, if any. If 254 // no handlers return true from this method then the query will be 255 // automatically canceled with an error code of -1 delivered to the 256 // JavaScript onFailure callback. If this method returns true then a 257 // Callback method must be executed either in this method or asynchronously 258 // to complete the query. 259 /// OnQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id,const CefString & request,bool persistent,CefRefPtr<Callback> callback)260 virtual bool OnQuery(CefRefPtr<CefBrowser> browser, 261 CefRefPtr<CefFrame> frame, 262 int64 query_id, 263 const CefString& request, 264 bool persistent, 265 CefRefPtr<Callback> callback) { 266 return false; 267 } 268 269 /// 270 // Executed when a query has been canceled either explicitly using the 271 // JavaScript cancel function or implicitly due to browser destruction, 272 // navigation or renderer process termination. It will only be called for 273 // the single handler that returned true from OnQuery for the same 274 // |query_id|. No references to the associated Callback object should be 275 // kept after this method is called, nor should any Callback methods be 276 // executed. 277 /// OnQueryCanceled(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id)278 virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser, 279 CefRefPtr<CefFrame> frame, 280 int64 query_id) {} 281 ~Handler()282 virtual ~Handler() {} 283 }; 284 285 /// 286 // Create a new router with the specified configuration. 287 /// 288 static CefRefPtr<CefMessageRouterBrowserSide> Create( 289 const CefMessageRouterConfig& config); 290 291 /// 292 // Add a new query handler. If |first| is true it will be added as the first 293 // handler, otherwise it will be added as the last handler. Returns true if 294 // the handler is added successfully or false if the handler has already been 295 // added. Must be called on the browser process UI thread. The Handler object 296 // must either outlive the router or be removed before deletion. 297 /// 298 virtual bool AddHandler(Handler* handler, bool first) = 0; 299 300 /// 301 // Remove an existing query handler. Any pending queries associated with the 302 // handler will be canceled. Handler::OnQueryCanceled will be called and the 303 // associated JavaScript onFailure callback will be executed with an error 304 // code of -1. Returns true if the handler is removed successfully or false 305 // if the handler is not found. Must be called on the browser process UI 306 // thread. 307 /// 308 virtual bool RemoveHandler(Handler* handler) = 0; 309 310 /// 311 // Cancel all pending queries associated with either |browser| or |handler|. 312 // If both |browser| and |handler| are NULL all pending queries will be 313 // canceled. Handler::OnQueryCanceled will be called and the associated 314 // JavaScript onFailure callback will be executed in all cases with an error 315 // code of -1. 316 /// 317 virtual void CancelPending(CefRefPtr<CefBrowser> browser, 318 Handler* handler) = 0; 319 320 /// 321 // Returns the number of queries currently pending for the specified |browser| 322 // and/or |handler|. Either or both values may be empty. Must be called on the 323 // browser process UI thread. 324 /// 325 virtual int GetPendingCount(CefRefPtr<CefBrowser> browser, 326 Handler* handler) = 0; 327 328 // The below methods should be called from other CEF handlers. They must be 329 // called exactly as documented for the router to function correctly. 330 331 /// 332 // Call from CefLifeSpanHandler::OnBeforeClose. Any pending queries associated 333 // with |browser| will be canceled and Handler::OnQueryCanceled will be 334 // called. No JavaScript callbacks will be executed since this indicates 335 // destruction of the browser. 336 /// 337 virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) = 0; 338 339 /// 340 // Call from CefRequestHandler::OnRenderProcessTerminated. Any pending queries 341 // associated with |browser| will be canceled and Handler::OnQueryCanceled 342 // will be called. No JavaScript callbacks will be executed since this 343 // indicates destruction of the context. 344 /// 345 virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) = 0; 346 347 /// 348 // Call from CefRequestHandler::OnBeforeBrowse only if the navigation is 349 // allowed to proceed. If |frame| is the main frame then any pending queries 350 // associated with |browser| will be canceled and Handler::OnQueryCanceled 351 // will be called. No JavaScript callbacks will be executed since this 352 // indicates destruction of the context. 353 /// 354 virtual void OnBeforeBrowse(CefRefPtr<CefBrowser> browser, 355 CefRefPtr<CefFrame> frame) = 0; 356 357 /// 358 // Call from CefClient::OnProcessMessageReceived. Returns true if the message 359 // is handled by this router or false otherwise. 360 /// 361 virtual bool OnProcessMessageReceived( 362 CefRefPtr<CefBrowser> browser, 363 CefRefPtr<CefFrame> frame, 364 CefProcessId source_process, 365 CefRefPtr<CefProcessMessage> message) = 0; 366 367 protected: 368 // Protect against accidental deletion of this object. 369 friend class base::RefCountedThreadSafe<CefMessageRouterBrowserSide>; ~CefMessageRouterBrowserSide()370 virtual ~CefMessageRouterBrowserSide() {} 371 }; 372 373 /// 374 // Implements the renderer side of query routing. The methods of this class must 375 // be called on the render process main thread. 376 /// 377 class CefMessageRouterRendererSide 378 : public base::RefCountedThreadSafe<CefMessageRouterRendererSide> { 379 public: 380 /// 381 // Create a new router with the specified configuration. 382 /// 383 static CefRefPtr<CefMessageRouterRendererSide> Create( 384 const CefMessageRouterConfig& config); 385 386 /// 387 // Returns the number of queries currently pending for the specified |browser| 388 // and/or |context|. Either or both values may be empty. 389 /// 390 virtual int GetPendingCount(CefRefPtr<CefBrowser> browser, 391 CefRefPtr<CefV8Context> context) = 0; 392 393 // The below methods should be called from other CEF handlers. They must be 394 // called exactly as documented for the router to function correctly. 395 396 /// 397 // Call from CefRenderProcessHandler::OnContextCreated. Registers the 398 // JavaScripts functions with the new context. 399 /// 400 virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, 401 CefRefPtr<CefFrame> frame, 402 CefRefPtr<CefV8Context> context) = 0; 403 404 /// 405 // Call from CefRenderProcessHandler::OnContextReleased. Any pending queries 406 // associated with the released context will be canceled and 407 // Handler::OnQueryCanceled will be called in the browser process. 408 /// 409 virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, 410 CefRefPtr<CefFrame> frame, 411 CefRefPtr<CefV8Context> context) = 0; 412 413 /// 414 // Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true 415 // if the message is handled by this router or false otherwise. 416 /// 417 virtual bool OnProcessMessageReceived( 418 CefRefPtr<CefBrowser> browser, 419 CefRefPtr<CefFrame> frame, 420 CefProcessId source_process, 421 CefRefPtr<CefProcessMessage> message) = 0; 422 423 protected: 424 // Protect against accidental deletion of this object. 425 friend class base::RefCountedThreadSafe<CefMessageRouterRendererSide>; ~CefMessageRouterRendererSide()426 virtual ~CefMessageRouterRendererSide() {} 427 }; 428 429 #endif // CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_ 430