cef/include/wrapper/cef_message_router.h
Marshall Greenblatt e411b513be Add CefFrameHandler callbacks for tracking CefFrame lifespan (see issue #2421)
See the new cef_frame_handler.h for complete usage documentation.

This change includes the following related enhancements:
- The newly added CefBrowser::IsValid method will return false (in the browser
  process) after CefLifeSpanHandler::OnBeforeClose is called.
- CefBrowser::GetMainFrame will return a valid object (in the browser process)
  until after CefLifeSpanHandler::OnBeforeClose is called.
- The main frame object will change during cross-origin navigation or
  re-navigation after renderer process termination. During that time,
  GetMainFrame will return the new/pending frame (in the browser process) and
  any messages that arrive for the new/pending frame will be correctly
  attributed in OnProcessMessageReceived.
- Commands to be executed in the renderer process that may fail during early
  frame initialization (ExecuteJavaScript, LoadRequest, etc.) will now be
  queued until after the JavaScript context for the frame has been created.
- Logging has been added for any commands that are dropped because they arrived
  after frame detachment.
2021-05-31 18:58:27 -04:00

430 lines
18 KiB
C++

// 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 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,
CefRefPtr<CefFrame> frame,
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,
CefRefPtr<CefFrame> frame,
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_