// 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, OnProcessMessageReceived, // 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 then 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_t query_id, // CefRefPtr browser, // CefRefPtr frame, // const CefString& request, // bool persistent, // CefRefPtr 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; /// /// Messages of size (in bytes) larger than this threshold will be sent via /// shared memory region. /// size_t message_size_threshold; }; /// /// 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 { 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: using Callback = CefMessageRouterBrowserSide::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 browser, CefRefPtr frame, int64_t query_id, const CefString& request, bool persistent, CefRefPtr 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 browser, CefRefPtr frame, int64_t query_id) {} virtual ~Handler() {} }; /// /// Create a new router with the specified configuration. /// static CefRefPtr 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 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 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 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 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 browser, CefRefPtr frame) = 0; /// /// Call from CefClient::OnProcessMessageReceived. Returns true if the message /// is handled by this router or false otherwise. /// virtual bool OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) = 0; protected: // Protect against accidental deletion of this object. friend class base::RefCountedThreadSafe; 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 { public: /// /// Create a new router with the specified configuration. /// static CefRefPtr 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 browser, CefRefPtr 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 browser, CefRefPtr frame, CefRefPtr 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 browser, CefRefPtr frame, CefRefPtr context) = 0; /// /// Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true /// if the message is handled by this router or false otherwise. /// virtual bool OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) = 0; protected: // Protect against accidental deletion of this object. friend class base::RefCountedThreadSafe; virtual ~CefMessageRouterRendererSide() {} }; #endif // CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_