diff --git a/BUILD.gn b/BUILD.gn index 58986eda5..16b519064 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -620,6 +620,8 @@ source_set("libcef_static") { "libcef/browser/frame_service_base.h", "libcef/browser/global_preference_manager_impl.cc", "libcef/browser/global_preference_manager_impl.h", + "libcef/browser/hang_monitor.cc", + "libcef/browser/hang_monitor.h", "libcef/browser/image_impl.cc", "libcef/browser/image_impl.h", "libcef/browser/iothread_state.cc", diff --git a/cef_paths.gypi b/cef_paths.gypi index afd86ad2b..330443311 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -8,7 +8,7 @@ # by hand. See the translator.README.txt file in the tools directory for # more information. # -# $hash=25599539f43226aac01bfcb74f19ac74217aee9a$ +# $hash=3b28f3236c16d2b776a44674ae3bae0a070e9f27$ # { @@ -85,6 +85,7 @@ 'include/cef_task.h', 'include/cef_thread.h', 'include/cef_trace.h', + 'include/cef_unresponsive_process_callback.h', 'include/cef_urlrequest.h', 'include/cef_v8.h', 'include/cef_values.h', @@ -189,6 +190,7 @@ 'include/capi/cef_task_capi.h', 'include/capi/cef_thread_capi.h', 'include/capi/cef_trace_capi.h', + 'include/capi/cef_unresponsive_process_callback_capi.h', 'include/capi/cef_urlrequest_capi.h', 'include/capi/cef_v8_capi.h', 'include/capi/cef_values_capi.h', @@ -508,6 +510,8 @@ 'libcef_dll/cpptoc/urlrequest_cpptoc.h', 'libcef_dll/ctocpp/urlrequest_client_ctocpp.cc', 'libcef_dll/ctocpp/urlrequest_client_ctocpp.h', + 'libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.cc', + 'libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.h', 'libcef_dll/ctocpp/v8accessor_ctocpp.cc', 'libcef_dll/ctocpp/v8accessor_ctocpp.h', 'libcef_dll/ctocpp/v8array_buffer_release_callback_ctocpp.cc', @@ -836,6 +840,8 @@ 'libcef_dll/ctocpp/urlrequest_ctocpp.h', 'libcef_dll/cpptoc/urlrequest_client_cpptoc.cc', 'libcef_dll/cpptoc/urlrequest_client_cpptoc.h', + 'libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.cc', + 'libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h', 'libcef_dll/cpptoc/v8accessor_cpptoc.cc', 'libcef_dll/cpptoc/v8accessor_cpptoc.h', 'libcef_dll/cpptoc/v8array_buffer_release_callback_cpptoc.cc', diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 9a5b22779..4f034a78c 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -220,6 +220,8 @@ 'tests/shared/browser/util_win.h', ], 'cefclient_sources_browser': [ + 'tests/cefclient/browser/base_client_handler.cc', + 'tests/cefclient/browser/base_client_handler.h', 'tests/cefclient/browser/binary_transfer_test.cc', 'tests/cefclient/browser/binary_transfer_test.h', 'tests/cefclient/browser/binding_test.cc', @@ -240,10 +242,11 @@ 'tests/cefclient/browser/client_prefs.cc', 'tests/cefclient/browser/client_prefs.h', 'tests/cefclient/browser/client_types.h', - 'tests/cefclient/browser/default_client_handler.cc', 'tests/cefclient/browser/default_client_handler.h', 'tests/cefclient/browser/dialog_test.cc', 'tests/cefclient/browser/dialog_test.h', + 'tests/cefclient/browser/hang_test.cc', + 'tests/cefclient/browser/hang_test.h', 'tests/cefclient/browser/image_cache.cc', 'tests/cefclient/browser/image_cache.h', 'tests/cefclient/browser/main_context.cc', @@ -309,11 +312,11 @@ 'tests/cefclient/renderer/performance_test_tests.cc', ], 'cefclient_sources_resources': [ + 'tests/cefclient/resources/binary_transfer.html', 'tests/cefclient/resources/binding.html', 'tests/cefclient/resources/dialogs.html', - 'tests/cefclient/resources/draggable.html', + 'tests/cefclient/resources/hang.html', 'tests/cefclient/resources/ipc_performance.html', - 'tests/cefclient/resources/binary_transfer.html', 'tests/cefclient/resources/localstorage.html', 'tests/cefclient/resources/logo.png', 'tests/cefclient/resources/media_router.html', diff --git a/include/capi/cef_app_capi.h b/include/capi/cef_app_capi.h index a85645e4a..0ea36a004 100644 --- a/include/capi/cef_app_capi.h +++ b/include/capi/cef_app_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=b9a2bad4a30bcb99384197c9f7409116dc5b376e$ +// $hash=dfa0d4d2da319b2fd5e92324fd14301b500ceb5c$ // #ifndef CEF_INCLUDE_CAPI_CEF_APP_CAPI_H_ @@ -137,15 +137,26 @@ CEF_EXPORT int cef_execute_process(const cef_main_args_t* args, /// true (1) if initialization succeeds. Returns false (0) if initialization /// fails or if early exit is desired (for example, due to process singleton /// relaunch behavior). If this function returns false (0) then the application -/// should exit immediately without calling any other CEF functions. The -/// |windows_sandbox_info| parameter is only used on Windows and may be NULL -/// (see cef_sandbox_win.h for details). +/// should exit immediately without calling any other CEF functions except, +/// optionally, CefGetErrorCode. The |windows_sandbox_info| parameter is only +/// used on Windows and may be NULL (see cef_sandbox_win.h for details). /// CEF_EXPORT int cef_initialize(const cef_main_args_t* args, const struct _cef_settings_t* settings, cef_app_t* application, void* windows_sandbox_info); +/// +/// This function can optionally be called on the main application thread after +/// CefInitialize to retrieve the initialization exit code. When CefInitialize +/// returns true (1) the exit code will be 0 (CEF_RESULT_CODE_NORMAL_EXIT). +/// Otherwise, see cef_resultcode_t for possible exit code values including +/// browser process initialization errors and normal early exit conditions (such +/// as CEF_RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED for process singleton +/// relaunch behavior). +/// +CEF_EXPORT int cef_get_exit_code(void); + /// /// This function should be called on the main application thread to shut down /// the CEF browser process before the application exits. Do not call any other diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index 4300126fc..5f9593eaa 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=e7f9480661f77931890085d6c5bf23d9842212e2$ +// $hash=abcb584dbf5965834f415a0f2daeda3e361696b2$ // #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_ @@ -974,6 +974,17 @@ typedef struct _cef_browser_host_t { struct _cef_browser_host_t* self, int command_id, cef_window_open_disposition_t disposition); + + /// + /// Returns true (1) if the render process associated with this browser is + /// currently unresponsive as indicated by a lack of input event processing + /// for at least 15 seconds. To receive associated state change notifications + /// and optionally handle an unresponsive render process implement + /// cef_request_handler_t::OnRenderProcessUnresponsive. This function can only + /// be called on the UI thread. + /// + int(CEF_CALLBACK* is_render_process_unresponsive)( + struct _cef_browser_host_t* self); } cef_browser_host_t; /// diff --git a/include/capi/cef_request_handler_capi.h b/include/capi/cef_request_handler_capi.h index 1e1974b39..9bea19486 100644 --- a/include/capi/cef_request_handler_capi.h +++ b/include/capi/cef_request_handler_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=3b59c0bc014d773dedc24649071141ee2dd2125c$ +// $hash=2e8b5c5107f61e3d4c333dc02c76a9f30cd0cf83$ // #ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_ @@ -48,6 +48,7 @@ #include "include/capi/cef_request_capi.h" #include "include/capi/cef_resource_request_handler_capi.h" #include "include/capi/cef_ssl_info_capi.h" +#include "include/capi/cef_unresponsive_process_callback_capi.h" #include "include/capi/cef_x509_certificate_capi.h" #ifdef __cplusplus @@ -222,14 +223,53 @@ typedef struct _cef_request_handler_t { void(CEF_CALLBACK* on_render_view_ready)(struct _cef_request_handler_t* self, struct _cef_browser_t* browser); + /// + /// Called on the browser process UI thread when the render process is + /// unresponsive as indicated by a lack of input event processing for at least + /// 15 seconds. Return false (0) for the default behavior which is an + /// indefinite wait with the Alloy runtime or display of the "Page + /// unresponsive" dialog with the Chrome runtime. Return true (1) and don't + /// execute the callback for an indefinite wait without display of the Chrome + /// runtime dialog. Return true (1) and call + /// cef_unresponsive_process_callback_t::Wait either in this function or at a + /// later time to reset the wait timer, potentially triggering another call to + /// this function if the process remains unresponsive. Return true (1) and + /// call cef_unresponsive_process_callback_t:: Terminate either in this + /// function or at a later time to terminate the unresponsive process, + /// resulting in a call to OnRenderProcessTerminated. + /// OnRenderProcessResponsive will be called if the process becomes responsive + /// after this function is called. This functionality depends on the hang + /// monitor which can be disabled by passing the `--disable-hang-monitor` + /// command-line flag. + /// + int(CEF_CALLBACK* on_render_process_unresponsive)( + struct _cef_request_handler_t* self, + struct _cef_browser_t* browser, + struct _cef_unresponsive_process_callback_t* callback); + + /// + /// Called on the browser process UI thread when the render process becomes + /// responsive after previously being unresponsive. See documentation on + /// OnRenderProcessUnresponsive. + /// + void(CEF_CALLBACK* on_render_process_responsive)( + struct _cef_request_handler_t* self, + struct _cef_browser_t* browser); + /// /// Called on the browser process UI thread when the render process terminates - /// unexpectedly. |status| indicates how the process terminated. + /// unexpectedly. |status| indicates how the process terminated. |error_code| + /// and |error_string| represent the error that would be displayed in Chrome's + /// "Aw, Snap!" view. Possible |error_code| values include cef_resultcode_t + /// non-normal exit values and platform-specific crash values (for example, a + /// Posix signal or Windows hardware exception). /// void(CEF_CALLBACK* on_render_process_terminated)( struct _cef_request_handler_t* self, struct _cef_browser_t* browser, - cef_termination_status_t status); + cef_termination_status_t status, + int error_code, + const cef_string_t* error_string); /// /// Called on the browser process UI thread when the window.document object of diff --git a/include/capi/cef_unresponsive_process_callback_capi.h b/include/capi/cef_unresponsive_process_callback_capi.h new file mode 100644 index 000000000..0eba3778f --- /dev/null +++ b/include/capi/cef_unresponsive_process_callback_capi.h @@ -0,0 +1,74 @@ +// Copyright (c) 2024 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +// $hash=9ad38f2709d9e3b1bd0e99c279b0497b8aa4c82a$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_UNRESPONSIVE_PROCESS_CALLBACK_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_UNRESPONSIVE_PROCESS_CALLBACK_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Callback structure for asynchronous handling of an unresponsive process. +/// +typedef struct _cef_unresponsive_process_callback_t { + /// + /// Base structure. + /// + cef_base_ref_counted_t base; + + /// + /// Reset the timeout for the unresponsive process. + /// + void(CEF_CALLBACK* wait)(struct _cef_unresponsive_process_callback_t* self); + + /// + /// Terminate the unresponsive process. + /// + void(CEF_CALLBACK* terminate)( + struct _cef_unresponsive_process_callback_t* self); +} cef_unresponsive_process_callback_t; + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_UNRESPONSIVE_PROCESS_CALLBACK_CAPI_H_ diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index f852a2d7a..3eda5c90f 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -42,13 +42,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "93050a5111ee3131f5589bd7d3be5376539a64c2" +#define CEF_API_HASH_UNIVERSAL "6c5009ba08bb760e60f7e17845c532b30f19fbae" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "ebcca93f53d935802a4cd02606864ff1eb1a1baa" +#define CEF_API_HASH_PLATFORM "09f5a8a3088d2a8384c648be2047e0cb25b1221e" #elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "a2c1ee5ae59a181773019412084903e4824aa300" +#define CEF_API_HASH_PLATFORM "ddee29d0c0de65874ff50f3ff7bc5de51089b9eb" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "915ea22829d9814cb6481891de4619f0aafda72f" +#define CEF_API_HASH_PLATFORM "a27764fa2599737204faad84b00c31bb32dba54f" #endif #ifdef __cplusplus diff --git a/include/cef_app.h b/include/cef_app.h index f52280cf1..f011d02fb 100644 --- a/include/cef_app.h +++ b/include/cef_app.h @@ -71,9 +71,9 @@ int CefExecuteProcess(const CefMainArgs& args, /// true if initialization succeeds. Returns false if initialization fails or if /// early exit is desired (for example, due to process singleton relaunch /// behavior). If this function returns false then the application should exit -/// immediately without calling any other CEF functions. The -/// |windows_sandbox_info| parameter is only used on Windows and may be NULL -/// (see cef_sandbox_win.h for details). +/// immediately without calling any other CEF functions except, optionally, +/// CefGetErrorCode. The |windows_sandbox_info| parameter is only used on +/// Windows and may be NULL (see cef_sandbox_win.h for details). /// /*--cef(api_hash_check,optional_param=application, optional_param=windows_sandbox_info)--*/ @@ -82,6 +82,18 @@ bool CefInitialize(const CefMainArgs& args, CefRefPtr application, void* windows_sandbox_info); +/// +/// This function can optionally be called on the main application thread after +/// CefInitialize to retrieve the initialization exit code. When CefInitialize +/// returns true the exit code will be 0 (CEF_RESULT_CODE_NORMAL_EXIT). +/// Otherwise, see cef_resultcode_t for possible exit code values including +/// browser process initialization errors and normal early exit conditions (such +/// as CEF_RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED for process singleton +/// relaunch behavior). +/// +/*--cef()--*/ +int CefGetExitCode(); + /// /// This function should be called on the main application thread to shut down /// the CEF browser process before the application exits. Do not call any diff --git a/include/cef_browser.h b/include/cef_browser.h index 4082b96c4..916edfe60 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -1007,6 +1007,17 @@ class CefBrowserHost : public virtual CefBaseRefCounted { virtual void ExecuteChromeCommand( int command_id, cef_window_open_disposition_t disposition) = 0; + + /// + /// Returns true if the render process associated with this browser is + /// currently unresponsive as indicated by a lack of input event processing + /// for at least 15 seconds. To receive associated state change notifications + /// and optionally handle an unresponsive render process implement + /// CefRequestHandler::OnRenderProcessUnresponsive. This method can only be + /// called on the UI thread. + /// + /*--cef()--*/ + virtual bool IsRenderProcessUnresponsive() = 0; }; #endif // CEF_INCLUDE_CEF_BROWSER_H_ diff --git a/include/cef_request_handler.h b/include/cef_request_handler.h index edeefbd1d..8c24ec7bc 100644 --- a/include/cef_request_handler.h +++ b/include/cef_request_handler.h @@ -48,6 +48,7 @@ #include "include/cef_request.h" #include "include/cef_resource_request_handler.h" #include "include/cef_ssl_info.h" +#include "include/cef_unresponsive_process_callback.h" #include "include/cef_x509_certificate.h" /// @@ -221,14 +222,52 @@ class CefRequestHandler : public virtual CefBaseRefCounted { /*--cef()--*/ virtual void OnRenderViewReady(CefRefPtr browser) {} + /// + /// Called on the browser process UI thread when the render process is + /// unresponsive as indicated by a lack of input event processing for at + /// least 15 seconds. Return false for the default behavior which is an + /// indefinite wait with the Alloy runtime or display of the "Page + /// unresponsive" dialog with the Chrome runtime. Return true and don't + /// execute the callback for an indefinite wait without display of the Chrome + /// runtime dialog. Return true and call CefUnresponsiveProcessCallback::Wait + /// either in this method or at a later time to reset the wait timer, + /// potentially triggering another call to this method if the process remains + /// unresponsive. Return true and call CefUnresponsiveProcessCallback:: + /// Terminate either in this method or at a later time to terminate the + /// unresponsive process, resulting in a call to OnRenderProcessTerminated. + /// OnRenderProcessResponsive will be called if the process becomes responsive + /// after this method is called. This functionality depends on the hang + /// monitor which can be disabled by passing the `--disable-hang-monitor` + /// command-line flag. + /// + /*--cef()--*/ + virtual bool OnRenderProcessUnresponsive( + CefRefPtr browser, + CefRefPtr callback) { + return false; + } + + /// + /// Called on the browser process UI thread when the render process becomes + /// responsive after previously being unresponsive. See documentation on + /// OnRenderProcessUnresponsive. + /// + /*--cef()--*/ + virtual void OnRenderProcessResponsive(CefRefPtr browser) {} + /// /// Called on the browser process UI thread when the render process - /// terminates unexpectedly. |status| indicates how the process - /// terminated. + /// terminates unexpectedly. |status| indicates how the process terminated. + /// |error_code| and |error_string| represent the error that would be + /// displayed in Chrome's "Aw, Snap!" view. Possible |error_code| values + /// include cef_resultcode_t non-normal exit values and platform-specific + /// crash values (for example, a Posix signal or Windows hardware exception). /// /*--cef()--*/ virtual void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) {} + TerminationStatus status, + int error_code, + const CefString& error_string) {} /// /// Called on the browser process UI thread when the window.document object of diff --git a/include/cef_unresponsive_process_callback.h b/include/cef_unresponsive_process_callback.h new file mode 100644 index 000000000..436e4a1bd --- /dev/null +++ b/include/cef_unresponsive_process_callback.h @@ -0,0 +1,62 @@ +// Copyright (c) 2024 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 must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_UNRESPONSIVE_PROCESS_CALLBACK_H_ +#define CEF_INCLUDE_CEF_UNRESPONSIVE_PROCESS_CALLBACK_H_ +#pragma once + +#include "include/cef_base.h" + +/// +/// Callback interface for asynchronous handling of an unresponsive process. +/// +/*--cef(source=library)--*/ +class CefUnresponsiveProcessCallback : public virtual CefBaseRefCounted { + public: + /// + /// Reset the timeout for the unresponsive process. + /// + /*--cef()--*/ + virtual void Wait() = 0; + + /// + /// Terminate the unresponsive process. + /// + /*--cef()--*/ + virtual void Terminate() = 0; +}; + +#endif // CEF_INCLUDE_CEF_UNRESPONSIVE_PROCESS_CALLBACK_H_ diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 63272e32e..01c0123b6 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -917,6 +917,16 @@ typedef enum { /// Out of memory. Some platforms may use TS_PROCESS_CRASHED instead. /// TS_PROCESS_OOM, + + /// + /// Child process never launched. + /// + TS_LAUNCH_FAILED, + + /// + /// On Windows, the OS terminated the process due to code integrity failure. + /// + TS_INTEGRITY_FAILURE, } cef_termination_status_t; /// @@ -1027,6 +1037,100 @@ typedef enum { CERT_STATUS_CT_COMPLIANCE_FAILED = 1 << 20, } cef_cert_status_t; +/// +/// Process result codes. This is not a comprehensive list, as result codes +/// might also include platform-specific crash values (Posix signal or Windows +/// hardware exception), or internal-only implementation values. +/// +typedef enum { + // The following values should be kept in sync with Chromium's + // content::ResultCode type. + + CEF_RESULT_CODE_NORMAL_EXIT, + + /// Process was killed by user or system. + CEF_RESULT_CODE_KILLED, + + /// Process hung. + CEF_RESULT_CODE_HUNG, + + /// A bad message caused the process termination. + CEF_RESULT_CODE_KILLED_BAD_MESSAGE, + + /// The GPU process exited because initialization failed. + CEF_RESULT_CODE_GPU_DEAD_ON_ARRIVAL, + + // The following values should be kept in sync with Chromium's + // chrome::ResultCode type. Unused chrome values are excluded. + + CEF_RESULT_CODE_CHROME_FIRST, + + /// A critical chrome file is missing. + CEF_RESULT_CODE_MISSING_DATA = 7, + + /// Command line parameter is not supported. + CEF_RESULT_CODE_UNSUPPORTED_PARAM = 13, + + /// The profile was in use on another host. + CEF_RESULT_CODE_PROFILE_IN_USE = 21, + + /// Failed to pack an extension via the command line. + CEF_RESULT_CODE_PACK_EXTENSION_ERROR = 22, + + /// The browser process exited early by passing the command line to another + /// running browser. + CEF_RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED = 24, + + /// A browser process was sandboxed. This should never happen. + CEF_RESULT_CODE_INVALID_SANDBOX_STATE = 31, + + /// Cloud policy enrollment failed or was given up by user. + CEF_RESULT_CODE_CLOUD_POLICY_ENROLLMENT_FAILED = 32, + + /// The GPU process was terminated due to context lost. + CEF_RESULT_CODE_GPU_EXIT_ON_CONTEXT_LOST = 34, + + /// An early startup command was executed and the browser must exit. + CEF_RESULT_CODE_NORMAL_EXIT_PACK_EXTENSION_SUCCESS = 36, + + /// The browser process exited because system resources are exhausted. The + /// system state can't be recovered and will be unstable. + CEF_RESULT_CODE_SYSTEM_RESOURCE_EXHAUSTED = 37, + + CEF_RESULT_CODE_CHROME_LAST = 39, + + // The following values should be kept in sync with Chromium's + // sandbox::TerminationCodes type. + + CEF_RESULT_CODE_SANDBOX_FATAL_FIRST = 7006, + + /// Windows sandbox could not set the integrity level. + CEF_RESULT_CODE_SANDBOX_FATAL_INTEGRITY = CEF_RESULT_CODE_SANDBOX_FATAL_FIRST, + + /// Windows sandbox could not lower the token. + CEF_RESULT_CODE_SANDBOX_FATAL_DROPTOKEN, + + /// Windows sandbox failed to flush registry handles. + CEF_RESULT_CODE_SANDBOX_FATAL_FLUSHANDLES, + + /// Windows sandbox failed to forbid HCKU caching. + CEF_RESULT_CODE_SANDBOX_FATAL_CACHEDISABLE, + + /// Windows sandbox failed to close pending handles. + CEF_RESULT_CODE_SANDBOX_FATAL_CLOSEHANDLES, + + /// Windows sandbox could not set the mitigation policy. + CEF_RESULT_CODE_SANDBOX_FATAL_MITIGATION, + + /// Windows sandbox exceeded the job memory limit. + CEF_RESULT_CODE_SANDBOX_FATAL_MEMORY_EXCEEDED, + + /// Windows sandbox failed to warmup. + CEF_RESULT_CODE_SANDBOX_FATAL_WARMUP, + + CEF_RESULT_CODE_SANDBOX_FATAL_LAST, +} cef_resultcode_t; + /// /// The manner in which a link click should be opened. These constants match /// their equivalents in Chromium's window_open_disposition.h and should not be diff --git a/libcef/browser/alloy/alloy_browser_host_impl.cc b/libcef/browser/alloy/alloy_browser_host_impl.cc index 3fa30b79c..4ed837ab6 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.cc +++ b/libcef/browser/alloy/alloy_browser_host_impl.cc @@ -18,6 +18,7 @@ #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/context.h" #include "libcef/browser/devtools/devtools_manager.h" +#include "libcef/browser/hang_monitor.h" #include "libcef/browser/media_access_query.h" #include "libcef/browser/osr/osr_util.h" #include "libcef/browser/request_context_impl.h" @@ -1196,6 +1197,20 @@ void AlloyBrowserHostImpl::WebContentsCreated( std::move(platform_delegate), /*extension=*/nullptr); } +void AlloyBrowserHostImpl::RendererUnresponsive( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) { + hang_monitor::RendererUnresponsive(this, render_widget_host, + hang_monitor_restarter); +} + +void AlloyBrowserHostImpl::RendererResponsive( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host) { + hang_monitor::RendererResponsive(this, render_widget_host); +} + content::JavaScriptDialogManager* AlloyBrowserHostImpl::GetJavaScriptDialogManager(content::WebContents* source) { if (!javascript_dialog_manager_) { diff --git a/libcef/browser/alloy/alloy_browser_host_impl.h b/libcef/browser/alloy/alloy_browser_host_impl.h index 04fdd3ef3..2e3c8e78f 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.h +++ b/libcef/browser/alloy/alloy_browser_host_impl.h @@ -233,6 +233,13 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase, const std::string& frame_name, const GURL& target_url, content::WebContents* new_contents) override; + void RendererUnresponsive( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) override; + void RendererResponsive( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host) override; content::JavaScriptDialogManager* GetJavaScriptDialogManager( content::WebContents* source) override; void RunFileChooser(content::RenderFrameHost* render_frame_host, diff --git a/libcef/browser/browser_contents_delegate.cc b/libcef/browser/browser_contents_delegate.cc index 5a629c92c..ec43f0ff8 100644 --- a/libcef/browser/browser_contents_delegate.cc +++ b/libcef/browser/browser_contents_delegate.cc @@ -10,6 +10,8 @@ #include "libcef/browser/native/cursor_util.h" #include "libcef/common/frame_util.h" +#include "chrome/browser/ui/views/sad_tab_view.h" +#include "chrome/common/chrome_result_codes.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/browser/focused_node_details.h" #include "content/public/browser/keyboard_event_processing_result.h" @@ -25,6 +27,10 @@ #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h" #include "third_party/blink/public/mojom/widget/platform_widget.mojom-test-utils.h" +#if defined(OS_WIN) +#include "sandbox/win/src/sandbox_types.h" +#endif + using content::KeyboardEventProcessingResult; namespace { @@ -351,6 +357,22 @@ void CefBrowserContentsDelegate::RenderViewReady() { void CefBrowserContentsDelegate::PrimaryMainFrameRenderProcessGone( base::TerminationStatus status) { + static_assert(static_cast(CEF_RESULT_CODE_CHROME_FIRST) == + static_cast(chrome::RESULT_CODE_CHROME_START), + "enum mismatch"); + static_assert(static_cast(CEF_RESULT_CODE_CHROME_LAST) == + static_cast(chrome::RESULT_CODE_CHROME_LAST_CODE), + "enum mismatch"); + +#if defined(OS_WIN) + static_assert(static_cast(CEF_RESULT_CODE_SANDBOX_FATAL_FIRST) == + static_cast(sandbox::SBOX_FATAL_INTEGRITY), + "enum mismatch"); + static_assert(static_cast(CEF_RESULT_CODE_SANDBOX_FATAL_LAST) == + static_cast(sandbox::SBOX_FATAL_LAST), + "enum mismatch"); +#endif + cef_termination_status_t ts = TS_ABNORMAL_TERMINATION; if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) { ts = TS_PROCESS_WAS_KILLED; @@ -358,14 +380,22 @@ void CefBrowserContentsDelegate::PrimaryMainFrameRenderProcessGone( ts = TS_PROCESS_CRASHED; } else if (status == base::TERMINATION_STATUS_OOM) { ts = TS_PROCESS_OOM; + } else if (status == base::TERMINATION_STATUS_LAUNCH_FAILED) { + ts = TS_LAUNCH_FAILED; +#if BUILDFLAG(IS_WIN) + } else if (status == base::TERMINATION_STATUS_INTEGRITY_FAILURE) { + ts = TS_INTEGRITY_FAILURE; +#endif } else if (status != base::TERMINATION_STATUS_ABNORMAL_TERMINATION) { return; } if (auto c = client()) { if (auto handler = c->GetRequestHandler()) { + int error_code = web_contents()->GetCrashedErrorCode(); auto navigation_lock = browser_info_->CreateNavigationLock(); - handler->OnRenderProcessTerminated(browser(), ts); + handler->OnRenderProcessTerminated(browser(), ts, error_code, + SadTabView::ErrorToString(error_code)); } } } diff --git a/libcef/browser/browser_host_base.cc b/libcef/browser/browser_host_base.cc index cbcee7b8b..9fbbb3e87 100644 --- a/libcef/browser/browser_host_base.cc +++ b/libcef/browser/browser_host_base.cc @@ -9,6 +9,7 @@ #include "libcef/browser/browser_info_manager.h" #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/context.h" +#include "libcef/browser/hang_monitor.h" #include "libcef/browser/image_impl.h" #include "libcef/browser/navigation_entry_impl.h" #include "libcef/browser/printing/print_util.h" @@ -31,6 +32,8 @@ #include "content/public/browser/download_request_utils.h" #include "content/public/browser/file_select_listener.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" #include "ui/base/resource/resource_scale_factor.h" #include "ui/gfx/image/image_skia.h" #include "ui/shell_dialogs/select_file_policy.h" @@ -256,6 +259,11 @@ void CefBrowserHostBase::DestroyBrowser() { contents_delegate_->RemoveObserver(this); contents_delegate_->ObserveWebContents(nullptr); + if (unresponsive_process_callback_) { + hang_monitor::Detach(unresponsive_process_callback_); + unresponsive_process_callback_.reset(); + } + CefBrowserInfoManager::GetInstance()->RemoveBrowserInfo(browser_info_); browser_info_->SetBrowser(nullptr); } @@ -707,6 +715,22 @@ void CefBrowserHostBase::ExitFullscreen(bool will_cause_resize) { } } +bool CefBrowserHostBase::IsRenderProcessUnresponsive() { + if (!CEF_CURRENTLY_ON_UIT()) { + DCHECK(false) << "called on invalid thread"; + return false; + } + + if (auto* web_contents = GetWebContents()) { + if (auto* rvh = web_contents->GetRenderViewHost()) { + if (auto* rwh = rvh->GetWidget()) { + return rwh->IsCurrentlyUnresponsive(); + } + } + } + return false; +} + void CefBrowserHostBase::ReplaceMisspelling(const CefString& word) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( diff --git a/libcef/browser/browser_host_base.h b/libcef/browser/browser_host_base.h index cfbb86130..f3698e743 100644 --- a/libcef/browser/browser_host_base.h +++ b/libcef/browser/browser_host_base.h @@ -8,6 +8,7 @@ #include "include/cef_browser.h" #include "include/cef_client.h" +#include "include/cef_unresponsive_process_callback.h" #include "include/views/cef_browser_view.h" #include "libcef/browser/browser_contents_delegate.h" #include "libcef/browser/browser_info.h" @@ -238,6 +239,7 @@ class CefBrowserHostBase : public CefBrowserHost, void NotifyMoveOrResizeStarted() override; bool IsFullscreen() override; void ExitFullscreen(bool will_cause_resize) override; + bool IsRenderProcessUnresponsive() override; // CefBrowser methods: bool IsValid() override; @@ -341,6 +343,15 @@ class CefBrowserHostBase : public CefBrowserHost, } CefMediaStreamRegistrar* GetMediaStreamRegistrar(); + CefRefPtr unresponsive_process_callback() + const { + return unresponsive_process_callback_; + } + void set_unresponsive_process_callback( + CefRefPtr callback) { + unresponsive_process_callback_ = callback; + } + // Returns the Widget owner for the browser window. Only used with windowed // browsers. views::Widget* GetWindowWidget() const; @@ -391,6 +402,7 @@ class CefBrowserHostBase : public CefBrowserHost, // Only accessed on the UI thread. std::unique_ptr contents_delegate_; + CefRefPtr unresponsive_process_callback_; // Observers that want to be notified of changes to this object. // Only accessed on the UI thread. diff --git a/libcef/browser/chrome/browser_delegate.h b/libcef/browser/chrome/browser_delegate.h index 6ef90b5aa..7cf00ab8e 100644 --- a/libcef/browser/chrome/browser_delegate.h +++ b/libcef/browser/chrome/browser_delegate.h @@ -119,6 +119,21 @@ class BrowserDelegate : public content::WebContentsDelegate { return callback; } + // Same as RendererUnresponsive but returning false if unhandled. + virtual bool RendererUnresponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) { + return false; + } + + // Same as RendererResponsive but returning false if unhandled. + virtual bool RendererResponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host) { + return false; + } + // Optionally override support for the specified window feature of type // Browser::WindowFeature. virtual std::optional SupportsWindowFeature(int feature) const { diff --git a/libcef/browser/chrome/chrome_browser_delegate.cc b/libcef/browser/chrome/chrome_browser_delegate.cc index 21e4b8127..f4fb16952 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.cc +++ b/libcef/browser/chrome/chrome_browser_delegate.cc @@ -14,6 +14,7 @@ #include "libcef/browser/chrome/chrome_browser_host_impl.h" #include "libcef/browser/chrome/views/chrome_browser_view.h" #include "libcef/browser/chrome/views/chrome_child_window.h" +#include "libcef/browser/hang_monitor.h" #include "libcef/browser/media_access_query.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/views/browser_view_impl.h" @@ -361,6 +362,26 @@ ChromeBrowserDelegate::RequestMediaAccessPermissionEx( return callback; } +bool ChromeBrowserDelegate::RendererUnresponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) { + if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) { + return hang_monitor::RendererUnresponsive(browser.get(), render_widget_host, + hang_monitor_restarter); + } + return false; +} + +bool ChromeBrowserDelegate::RendererResponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host) { + if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) { + return hang_monitor::RendererResponsive(browser.get(), render_widget_host); + } + return false; +} + bool ChromeBrowserDelegate::SupportsFramelessPictureInPicture() const { if (!browser_->is_type_picture_in_picture()) { return false; diff --git a/libcef/browser/chrome/chrome_browser_delegate.h b/libcef/browser/chrome/chrome_browser_delegate.h index 04153e7fd..5b7dad330 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.h +++ b/libcef/browser/chrome/chrome_browser_delegate.h @@ -76,6 +76,13 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) override; + bool RendererUnresponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) override; + bool RendererResponsiveEx( + content::WebContents* source, + content::RenderWidgetHost* render_widget_host) override; std::optional SupportsWindowFeature(int feature) const override; bool SupportsDraggableRegion() const override; const std::optional GetDraggableRegion() const override; diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index a20b70269..5045fd221 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -34,6 +34,9 @@ namespace { CefContext* g_context = nullptr; +// Invalid value before CefInitialize is called. +int g_exit_code = -1; + #if DCHECK_IS_ON() // When the process terminates check if CefShutdown() has been called. class CefShutdownChecker { @@ -309,8 +312,11 @@ bool CefInitialize(const CefMainArgs& args, g_context = new CefContext(); // Initialize the global context. - if (!g_context->Initialize(args, settings, application, - windows_sandbox_info)) { + const bool initialized = + g_context->Initialize(args, settings, application, windows_sandbox_info); + g_exit_code = g_context->exit_code(); + + if (!initialized) { // Initialization failed. Delete the global context object. delete g_context; g_context = nullptr; @@ -320,6 +326,11 @@ bool CefInitialize(const CefMainArgs& args, return true; } +int CefGetExitCode() { + DCHECK_NE(g_exit_code, -1) << "invalid call to CefGetExitCode"; + return g_exit_code; +} + void CefShutdown() { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { @@ -477,10 +488,14 @@ bool CefContext::Initialize(const CefMainArgs& args, main_runner_ = std::make_unique( settings_.multi_threaded_message_loop, settings_.external_message_pump); - if (!main_runner_->Initialize( - &settings_, application, args, windows_sandbox_info, &initialized_, - base::BindOnce(&CefContext::OnContextInitialized, - base::Unretained(this)))) { + + const bool initialized = main_runner_->Initialize( + &settings_, application, args, windows_sandbox_info, &initialized_, + base::BindOnce(&CefContext::OnContextInitialized, + base::Unretained(this))); + exit_code_ = main_runner_->exit_code(); + + if (!initialized) { shutting_down_ = true; FinalizeShutdown(); return false; diff --git a/libcef/browser/context.h b/libcef/browser/context.h index fc06e28ae..2d29390f2 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -52,10 +52,13 @@ class CefContext { bool OnInitThread(); // Returns true if the context is initialized. - bool initialized() { return initialized_; } + bool initialized() const { return initialized_; } // Returns true if the context is shutting down. - bool shutting_down() { return shutting_down_; } + bool shutting_down() const { return shutting_down_; } + + // Only valid after Initialize is called. + int exit_code() const { return exit_code_; } const CefSettings& settings() const { return settings_; } @@ -100,6 +103,7 @@ class CefContext { // Track context state. bool initialized_ = false; bool shutting_down_ = false; + int exit_code_ = -1; // The thread on which the context was initialized. base::PlatformThreadId init_thread_id_ = 0; diff --git a/libcef/browser/hang_monitor.cc b/libcef/browser/hang_monitor.cc new file mode 100644 index 000000000..3cf1a439c --- /dev/null +++ b/libcef/browser/hang_monitor.cc @@ -0,0 +1,167 @@ +// Copyright 2024 The Chromium Embedded Framework Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +#include "libcef/browser/hang_monitor.h" + +#include "include/cef_client.h" +#include "libcef/browser/browser_host_base.h" + +#include "build/build_config.h" +#include "chrome/browser/hang_monitor/hang_crash_dump.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/common/result_codes.h" + +namespace hang_monitor { + +namespace { + +// Based on HungRendererDialogView::ForceCrashHungRenderer. +void ForceCrashHungRenderer(content::RenderWidgetHost* render_widget_host) { + content::RenderProcessHost* rph = render_widget_host->GetProcess(); + if (rph) { +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + // A generic |CrashDumpHungChildProcess()| is not implemented for Linux. + // Instead we send an explicit IPC to crash on the renderer's IO thread. + rph->ForceCrash(); +#else + // Try to generate a crash report for the hung process. + CrashDumpHungChildProcess(rph->GetProcess().Handle()); + rph->Shutdown(content::RESULT_CODE_HUNG); +#endif + } +} + +class CefUnresponsiveProcessCallbackImpl + : public CefUnresponsiveProcessCallback { + public: + CefUnresponsiveProcessCallbackImpl( + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) + : render_widget_host_(render_widget_host), + hang_monitor_restarter_(hang_monitor_restarter) {} + + CefUnresponsiveProcessCallbackImpl( + const CefUnresponsiveProcessCallbackImpl&) = delete; + CefUnresponsiveProcessCallbackImpl& operator=( + const CefUnresponsiveProcessCallbackImpl&) = delete; + + ~CefUnresponsiveProcessCallbackImpl() override { + // Do nothing on destruction. + } + + void Wait() override { ContinueNow(true); } + + void Terminate() override { ContinueNow(false); } + + void Detach() { + render_widget_host_ = nullptr; + hang_monitor_restarter_.Reset(); + } + + bool IsDetached() const { return !render_widget_host_; } + + private: + void ContinueNow(bool wait) { + if (CEF_CURRENTLY_ON_UIT()) { + if (!IsDetached()) { + RunNow(render_widget_host_, hang_monitor_restarter_, wait); + Detach(); + } + } else { + CEF_POST_TASK( + CEF_UIT, + base::BindOnce(&CefUnresponsiveProcessCallbackImpl::ContinueNow, this, + wait)); + } + } + + static void RunNow(content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter, + bool wait) { + CEF_REQUIRE_UIT(); + if (wait) { + hang_monitor_restarter.Run(); + } else { + ForceCrashHungRenderer(render_widget_host); + } + } + + content::RenderWidgetHost* render_widget_host_; + base::RepeatingClosure hang_monitor_restarter_; + + IMPLEMENT_REFCOUNTING(CefUnresponsiveProcessCallbackImpl); +}; + +bool ResetRendererCallback(CefBrowserHostBase* browser) { + CEF_REQUIRE_UIT(); + if (auto callback = browser->unresponsive_process_callback()) { + Detach(callback); + browser->set_unresponsive_process_callback(nullptr); + return true; + } + return false; +} + +CefRefPtr GetRequestHandler(CefBrowserHostBase* browser) { + if (auto client = browser->GetClient()) { + return client->GetRequestHandler(); + } + return nullptr; +} + +} // namespace + +bool RendererUnresponsive(CefBrowserHostBase* browser, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) { + // There should be no callback currently. + DCHECK(!browser->unresponsive_process_callback()); + + if (auto handler = GetRequestHandler(browser)) { + CefRefPtr callbackImpl( + new CefUnresponsiveProcessCallbackImpl(render_widget_host, + hang_monitor_restarter)); + if (!handler->OnRenderProcessUnresponsive(browser, callbackImpl.get())) { + if (!callbackImpl->IsDetached()) { + // Proceed with default handling. + callbackImpl->Detach(); + return false; + } else { + LOG(ERROR) << "Should return true from OnRenderProcessUnresponsive " + "when executing the callback"; + } + } + + // Proceed with client handling. The callback may already be executed, but + // we still want to wait for RendererResponsive. + browser->set_unresponsive_process_callback(callbackImpl.get()); + return true; + } + + // Proceed with default handling. + return false; +} + +bool RendererResponsive(CefBrowserHostBase* browser, + content::RenderWidgetHost* render_widget_host) { + // |handled| will be true if the client handled OnRenderProcessUnresponsive. + bool handled = ResetRendererCallback(browser); + + // Always execute the client callback. + if (auto handler = GetRequestHandler(browser)) { + handler->OnRenderProcessResponsive(browser); + } + + return handled; +} + +void Detach(CefRefPtr callback) { + CEF_REQUIRE_UIT(); + auto* callback_impl = + static_cast(callback.get()); + callback_impl->Detach(); +} + +} // namespace hang_monitor diff --git a/libcef/browser/hang_monitor.h b/libcef/browser/hang_monitor.h new file mode 100644 index 000000000..afed59d5e --- /dev/null +++ b/libcef/browser/hang_monitor.h @@ -0,0 +1,37 @@ +// Copyright 2024 The Chromium Embedded Framework Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_HANG_MONITOR_H_ +#define CEF_LIBCEF_BROWSER_HANG_MONITOR_H_ +#pragma once + +#include "include/cef_unresponsive_process_callback.h" + +#include "base/functional/callback.h" + +namespace content { +class RenderWidgetHost; +} + +class CefBrowserHostBase; + +namespace hang_monitor { + +// Called from WebContentsDelegate::RendererUnresponsive. +// Returns false for default handling. +bool RendererUnresponsive(CefBrowserHostBase* browser, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter); + +// Called from WebContentsDelegate::RendererResponsive. +// Returns false for default handling. +bool RendererResponsive(CefBrowserHostBase* browser, + content::RenderWidgetHost* render_widget_host); + +// Detach an existing callback object. +void Detach(CefRefPtr callback); + +} // namespace hang_monitor + +#endif // CEF_LIBCEF_BROWSER_HANG_MONITOR_H_ diff --git a/libcef/browser/main_runner.cc b/libcef/browser/main_runner.cc index 94822ebf8..26ab9b84a 100644 --- a/libcef/browser/main_runner.cc +++ b/libcef/browser/main_runner.cc @@ -245,20 +245,20 @@ bool CefMainRunner::Initialize(CefSettings* settings, settings->chrome_runtime ? RuntimeType::CHROME : RuntimeType::ALLOY, this, settings, application); - int exit_code = + exit_code_ = ContentMainInitialize(args, windows_sandbox_info, &settings->no_sandbox); - if (exit_code >= 0) { - LOG(ERROR) << "ContentMainInitialize failed with exit code " << exit_code; + if (exit_code_ >= 0) { + LOG(ERROR) << "ContentMainInitialize failed with exit code " << exit_code_; return false; } - exit_code = ContentMainRun(initialized, std::move(context_initialized)); - if (exit_code != content::RESULT_CODE_NORMAL_EXIT) { + exit_code_ = ContentMainRun(initialized, std::move(context_initialized)); + if (exit_code_ != content::RESULT_CODE_NORMAL_EXIT) { // Some exit codes are used to exit early, but are otherwise a normal // result. Don't log for those codes. if (!chrome::IsNormalResultCode( - static_cast(exit_code))) { - LOG(ERROR) << "ContentMainRun failed with exit code " << exit_code; + static_cast(exit_code_))) { + LOG(ERROR) << "ContentMainRun failed with exit code " << exit_code_; } return false; } diff --git a/libcef/browser/main_runner.h b/libcef/browser/main_runner.h index b7163adf4..d2cb3887a 100644 --- a/libcef/browser/main_runner.h +++ b/libcef/browser/main_runner.h @@ -42,6 +42,9 @@ class CefMainRunner : public CefMainRunnerHandler { bool* initialized, base::OnceClosure context_initialized); + // Only valid after Initialize is called. + int exit_code() const { return exit_code_; } + // Called from CefContext::Shutdown. void Shutdown(base::OnceClosure shutdown_on_ui_thread, base::OnceClosure finalize_shutdown); @@ -90,6 +93,8 @@ class CefMainRunner : public CefMainRunnerHandler { // Used to quit the current base::RunLoop. base::OnceClosure quit_callback_; + + int exit_code_ = -1; }; #endif // CEF_LIBCEF_BROWSER_MAIN_RUNNER_H_ diff --git a/libcef_dll/cpptoc/browser_host_cpptoc.cc b/libcef_dll/cpptoc/browser_host_cpptoc.cc index 97dee8d99..46277c6da 100644 --- a/libcef_dll/cpptoc/browser_host_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_host_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=c1afe37bef47447905f9dc6b6ba56f550e24021f$ +// $hash=790f88d9d22bbef9882470f0980f5e7e446d30c5$ // #include "libcef_dll/cpptoc/browser_host_cpptoc.h" @@ -1506,6 +1506,24 @@ browser_host_execute_chrome_command(struct _cef_browser_host_t* self, disposition); } +int CEF_CALLBACK +browser_host_is_render_process_unresponsive(struct _cef_browser_host_t* self) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return 0; + } + + // Execute + bool _retval = CefBrowserHostCppToC::Get(self)->IsRenderProcessUnresponsive(); + + // Return type: bool + return _retval; +} + } // namespace // CONSTRUCTOR - Do not edit by hand. @@ -1588,6 +1606,8 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() { GetStruct()->can_execute_chrome_command = browser_host_can_execute_chrome_command; GetStruct()->execute_chrome_command = browser_host_execute_chrome_command; + GetStruct()->is_render_process_unresponsive = + browser_host_is_render_process_unresponsive; } // DESTRUCTOR - Do not edit by hand. diff --git a/libcef_dll/cpptoc/request_handler_cpptoc.cc b/libcef_dll/cpptoc/request_handler_cpptoc.cc index 0c897e717..d7e1ae221 100644 --- a/libcef_dll/cpptoc/request_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/request_handler_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=4a0c9cb5d77d315e067d07ed633b623a8e12a1fc$ +// $hash=436eb7d254d6edc452b8b529758967d274fa2f85$ // #include "libcef_dll/cpptoc/request_handler_cpptoc.h" @@ -21,6 +21,7 @@ #include "libcef_dll/ctocpp/request_ctocpp.h" #include "libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h" #include "libcef_dll/ctocpp/sslinfo_ctocpp.h" +#include "libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h" #include "libcef_dll/ctocpp/x509certificate_ctocpp.h" #include "libcef_dll/shutdown_checker.h" @@ -345,10 +346,42 @@ request_handler_on_render_view_ready(struct _cef_request_handler_t* self, CefBrowserCToCpp::Wrap(browser)); } -void CEF_CALLBACK request_handler_on_render_process_terminated( +int CEF_CALLBACK request_handler_on_render_process_unresponsive( struct _cef_request_handler_t* self, cef_browser_t* browser, - cef_termination_status_t status) { + struct _cef_unresponsive_process_callback_t* callback) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return 0; + } + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) { + return 0; + } + // Verify param: callback; type: refptr_diff + DCHECK(callback); + if (!callback) { + return 0; + } + + // Execute + bool _retval = + CefRequestHandlerCppToC::Get(self)->OnRenderProcessUnresponsive( + CefBrowserCToCpp::Wrap(browser), + CefUnresponsiveProcessCallbackCToCpp::Wrap(callback)); + + // Return type: bool + return _retval; +} + +void CEF_CALLBACK request_handler_on_render_process_responsive( + struct _cef_request_handler_t* self, + cef_browser_t* browser) { shutdown_checker::AssertNotShutdown(); // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -363,9 +396,40 @@ void CEF_CALLBACK request_handler_on_render_process_terminated( return; } + // Execute + CefRequestHandlerCppToC::Get(self)->OnRenderProcessResponsive( + CefBrowserCToCpp::Wrap(browser)); +} + +void CEF_CALLBACK request_handler_on_render_process_terminated( + struct _cef_request_handler_t* self, + cef_browser_t* browser, + cef_termination_status_t status, + int error_code, + const cef_string_t* error_string) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return; + } + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) { + return; + } + // Verify param: error_string; type: string_byref_const + DCHECK(error_string); + if (!error_string) { + return; + } + // Execute CefRequestHandlerCppToC::Get(self)->OnRenderProcessTerminated( - CefBrowserCToCpp::Wrap(browser), status); + CefBrowserCToCpp::Wrap(browser), status, error_code, + CefString(error_string)); } void CEF_CALLBACK request_handler_on_document_available_in_main_frame( @@ -404,6 +468,10 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC() { GetStruct()->on_select_client_certificate = request_handler_on_select_client_certificate; GetStruct()->on_render_view_ready = request_handler_on_render_view_ready; + GetStruct()->on_render_process_unresponsive = + request_handler_on_render_process_unresponsive; + GetStruct()->on_render_process_responsive = + request_handler_on_render_process_responsive; GetStruct()->on_render_process_terminated = request_handler_on_render_process_terminated; GetStruct()->on_document_available_in_main_frame = diff --git a/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.cc b/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.cc new file mode 100644 index 000000000..cadbb2893 --- /dev/null +++ b/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=d31cea8b172629a308a7d54f8dd6fccff4cc8823$ +// + +#include "libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.h" +#include "libcef_dll/shutdown_checker.h" + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +void CEF_CALLBACK unresponsive_process_callback_wait( + struct _cef_unresponsive_process_callback_t* self) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return; + } + + // Execute + CefUnresponsiveProcessCallbackCppToC::Get(self)->Wait(); +} + +void CEF_CALLBACK unresponsive_process_callback_terminate( + struct _cef_unresponsive_process_callback_t* self) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return; + } + + // Execute + CefUnresponsiveProcessCallbackCppToC::Get(self)->Terminate(); +} + +} // namespace + +// CONSTRUCTOR - Do not edit by hand. + +CefUnresponsiveProcessCallbackCppToC::CefUnresponsiveProcessCallbackCppToC() { + GetStruct()->wait = unresponsive_process_callback_wait; + GetStruct()->terminate = unresponsive_process_callback_terminate; +} + +// DESTRUCTOR - Do not edit by hand. + +CefUnresponsiveProcessCallbackCppToC::~CefUnresponsiveProcessCallbackCppToC() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +CefRefPtr +CefCppToCRefCounted:: + UnwrapDerived(CefWrapperType type, cef_unresponsive_process_callback_t* s) { + DCHECK(false) << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType + CefCppToCRefCounted::kWrapperType = + WT_UNRESPONSIVE_PROCESS_CALLBACK; diff --git a/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.h b/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.h new file mode 100644 index 000000000..7fa933dac --- /dev/null +++ b/libcef_dll/cpptoc/unresponsive_process_callback_cpptoc.h @@ -0,0 +1,38 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=1816748407afd5599d8582de51b3289d1ea24a33$ +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_UNRESPONSIVE_PROCESS_CALLBACK_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_UNRESPONSIVE_PROCESS_CALLBACK_CPPTOC_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +#include "include/capi/cef_unresponsive_process_callback_capi.h" +#include "include/cef_unresponsive_process_callback.h" +#include "libcef_dll/cpptoc/cpptoc_ref_counted.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed DLL-side only. +class CefUnresponsiveProcessCallbackCppToC + : public CefCppToCRefCounted { + public: + CefUnresponsiveProcessCallbackCppToC(); + virtual ~CefUnresponsiveProcessCallbackCppToC(); +}; + +#endif // CEF_LIBCEF_DLL_CPPTOC_UNRESPONSIVE_PROCESS_CALLBACK_CPPTOC_H_ diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.cc b/libcef_dll/ctocpp/browser_host_ctocpp.cc index 5ff479803..101143043 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_host_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=736aa196beae8c3c35ef330eb78fb0908fccf70b$ +// $hash=da0fdd0a724301aa3ca12055bce970b9d3d0f708$ // #include "libcef_dll/ctocpp/browser_host_ctocpp.h" @@ -1296,6 +1296,24 @@ void CefBrowserHostCToCpp::ExecuteChromeCommand( _struct->execute_chrome_command(_struct, command_id, disposition); } +NO_SANITIZE("cfi-icall") +bool CefBrowserHostCToCpp::IsRenderProcessUnresponsive() { + shutdown_checker::AssertNotShutdown(); + + cef_browser_host_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, is_render_process_unresponsive)) { + return false; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->is_render_process_unresponsive(_struct); + + // Return type: bool + return _retval ? true : false; +} + // CONSTRUCTOR - Do not edit by hand. CefBrowserHostCToCpp::CefBrowserHostCToCpp() {} diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.h b/libcef_dll/ctocpp/browser_host_ctocpp.h index 5614ccf4e..849ee39a8 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.h +++ b/libcef_dll/ctocpp/browser_host_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=8b1e4268c2b68437b5022e9baf3f1c56bf036fe7$ +// $hash=ec03d4b0c8e59ba8f8eb3060e374b4d76e22669b$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_ @@ -139,6 +139,7 @@ class CefBrowserHostCToCpp : public CefCToCppRefCounted browser) { _struct->on_render_view_ready(_struct, CefBrowserCppToC::Wrap(browser)); } +NO_SANITIZE("cfi-icall") +bool CefRequestHandlerCToCpp::OnRenderProcessUnresponsive( + CefRefPtr browser, + CefRefPtr callback) { + shutdown_checker::AssertNotShutdown(); + + cef_request_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_render_process_unresponsive)) { + return false; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) { + return false; + } + // Verify param: callback; type: refptr_diff + DCHECK(callback.get()); + if (!callback.get()) { + return false; + } + + // Execute + int _retval = _struct->on_render_process_unresponsive( + _struct, CefBrowserCppToC::Wrap(browser), + CefUnresponsiveProcessCallbackCppToC::Wrap(callback)); + + // Return type: bool + return _retval ? true : false; +} + +NO_SANITIZE("cfi-icall") +void CefRequestHandlerCToCpp::OnRenderProcessResponsive( + CefRefPtr browser) { + shutdown_checker::AssertNotShutdown(); + + cef_request_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_render_process_responsive)) { + return; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) { + return; + } + + // Execute + _struct->on_render_process_responsive(_struct, + CefBrowserCppToC::Wrap(browser)); +} + NO_SANITIZE("cfi-icall") void CefRequestHandlerCToCpp::OnRenderProcessTerminated( CefRefPtr browser, - TerminationStatus status) { + TerminationStatus status, + int error_code, + const CefString& error_string) { shutdown_checker::AssertNotShutdown(); cef_request_handler_t* _struct = GetStruct(); @@ -358,10 +417,16 @@ void CefRequestHandlerCToCpp::OnRenderProcessTerminated( if (!browser.get()) { return; } + // Verify param: error_string; type: string_byref_const + DCHECK(!error_string.empty()); + if (error_string.empty()) { + return; + } // Execute - _struct->on_render_process_terminated( - _struct, CefBrowserCppToC::Wrap(browser), status); + _struct->on_render_process_terminated(_struct, + CefBrowserCppToC::Wrap(browser), status, + error_code, error_string.GetStruct()); } NO_SANITIZE("cfi-icall") diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.h b/libcef_dll/ctocpp/request_handler_ctocpp.h index bf84d5a20..3ea8e29a7 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.h +++ b/libcef_dll/ctocpp/request_handler_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=272831fa944a45a447a333f81c9e5501a3a7db64$ +// $hash=af82af05fec824761f5ff735ff6c9831938deb16$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_ @@ -74,8 +74,14 @@ class CefRequestHandlerCToCpp const X509CertificateList& certificates, CefRefPtr callback) override; void OnRenderViewReady(CefRefPtr browser) override; + bool OnRenderProcessUnresponsive( + CefRefPtr browser, + CefRefPtr callback) override; + void OnRenderProcessResponsive(CefRefPtr browser) override; void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override; + TerminationStatus status, + int error_code, + const CefString& error_string) override; void OnDocumentAvailableInMainFrame(CefRefPtr browser) override; }; diff --git a/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.cc b/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.cc new file mode 100644 index 000000000..1c05a841b --- /dev/null +++ b/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=c8f9d6f148a367be5eef1b3512ba023da0f9ac84$ +// + +#include "libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h" +#include "libcef_dll/shutdown_checker.h" + +// VIRTUAL METHODS - Body may be edited by hand. + +NO_SANITIZE("cfi-icall") void CefUnresponsiveProcessCallbackCToCpp::Wait() { + shutdown_checker::AssertNotShutdown(); + + cef_unresponsive_process_callback_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, wait)) { + return; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->wait(_struct); +} + +NO_SANITIZE("cfi-icall") +void CefUnresponsiveProcessCallbackCToCpp::Terminate() { + shutdown_checker::AssertNotShutdown(); + + cef_unresponsive_process_callback_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, terminate)) { + return; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->terminate(_struct); +} + +// CONSTRUCTOR - Do not edit by hand. + +CefUnresponsiveProcessCallbackCToCpp::CefUnresponsiveProcessCallbackCToCpp() {} + +// DESTRUCTOR - Do not edit by hand. + +CefUnresponsiveProcessCallbackCToCpp::~CefUnresponsiveProcessCallbackCToCpp() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +cef_unresponsive_process_callback_t* +CefCToCppRefCounted:: + UnwrapDerived(CefWrapperType type, CefUnresponsiveProcessCallback* c) { + DCHECK(false) << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType + CefCToCppRefCounted::kWrapperType = + WT_UNRESPONSIVE_PROCESS_CALLBACK; diff --git a/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h b/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h new file mode 100644 index 000000000..0ef109f85 --- /dev/null +++ b/libcef_dll/ctocpp/unresponsive_process_callback_ctocpp.h @@ -0,0 +1,42 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=4c04a490cc0609560e711890eeb286431675bf54$ +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_UNRESPONSIVE_PROCESS_CALLBACK_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_UNRESPONSIVE_PROCESS_CALLBACK_CTOCPP_H_ +#pragma once + +#if !defined(WRAPPING_CEF_SHARED) +#error This file can be included wrapper-side only +#endif + +#include "include/capi/cef_unresponsive_process_callback_capi.h" +#include "include/cef_unresponsive_process_callback.h" +#include "libcef_dll/ctocpp/ctocpp_ref_counted.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed wrapper-side only. +class CefUnresponsiveProcessCallbackCToCpp + : public CefCToCppRefCounted { + public: + CefUnresponsiveProcessCallbackCToCpp(); + virtual ~CefUnresponsiveProcessCallbackCToCpp(); + + // CefUnresponsiveProcessCallback methods. + void Wait() override; + void Terminate() override; +}; + +#endif // CEF_LIBCEF_DLL_CTOCPP_UNRESPONSIVE_PROCESS_CALLBACK_CTOCPP_H_ diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 3b40694bd..dca129c47 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=215fc5a9025e8b0cd3d1bc7259ad6e6ac53b2125$ +// $hash=716d8a4bb86c9ee9ebe8dfe28ec2c37411507830$ // #include "include/capi/cef_app_capi.h" @@ -118,6 +118,16 @@ CEF_EXPORT int cef_initialize(const cef_main_args_t* args, return _retval; } +CEF_EXPORT int cef_get_exit_code() { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = CefGetExitCode(); + + // Return type: simple + return _retval; +} + CEF_EXPORT void cef_shutdown() { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/libcef_dll/wrapper/libcef_dll_dylib.cc b/libcef_dll/wrapper/libcef_dll_dylib.cc index 1c5d1185d..704182047 100644 --- a/libcef_dll/wrapper/libcef_dll_dylib.cc +++ b/libcef_dll/wrapper/libcef_dll_dylib.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=f882e920463e6681b19a06c9d897b09b17e27bd3$ +// $hash=fd77a51eeea378bb56d43ba2995a0417d905c247$ // #include @@ -92,6 +92,7 @@ void* libcef_get_ptr(const char* path, const char* name) { struct libcef_pointers { decltype(&cef_execute_process) cef_execute_process; decltype(&cef_initialize) cef_initialize; + decltype(&cef_get_exit_code) cef_get_exit_code; decltype(&cef_shutdown) cef_shutdown; decltype(&cef_do_message_loop_work) cef_do_message_loop_work; decltype(&cef_run_message_loop) cef_run_message_loop; @@ -337,6 +338,7 @@ struct libcef_pointers { int libcef_init_pointers(const char* path) { INIT_ENTRY(cef_execute_process); INIT_ENTRY(cef_initialize); + INIT_ENTRY(cef_get_exit_code); INIT_ENTRY(cef_shutdown); INIT_ENTRY(cef_do_message_loop_work); INIT_ENTRY(cef_run_message_loop); @@ -590,6 +592,10 @@ int cef_initialize(const cef_main_args_t* args, windows_sandbox_info); } +NO_SANITIZE("cfi-icall") int cef_get_exit_code() { + return g_libcef_pointers.cef_get_exit_code(); +} + NO_SANITIZE("cfi-icall") void cef_shutdown() { g_libcef_pointers.cef_shutdown(); } diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 27906474d..000b020e3 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=f2b284f21cdb59e621ecddbe2f07392d24d48ddd$ +// $hash=23f943f8e59a48f29ba3095642bc05d7f987a2f2$ // #include "include/capi/cef_app_capi.h" @@ -106,6 +106,16 @@ CEF_GLOBAL bool CefInitialize(const CefMainArgs& args, return _retval ? true : false; } +NO_SANITIZE("cfi-icall") CEF_GLOBAL int CefGetExitCode() { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = cef_get_exit_code(); + + // Return type: simple + return _retval; +} + NO_SANITIZE("cfi-icall") CEF_GLOBAL void CefShutdown() { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/libcef_dll/wrapper_types.h b/libcef_dll/wrapper_types.h index d02e51107..dd2a666f4 100644 --- a/libcef_dll/wrapper_types.h +++ b/libcef_dll/wrapper_types.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=613fb0a830ebbd0ad913f60a482b8ba9d6f01ff4$ +// $hash=f250db632dc9f127b3aaff303d6105ca235ff462$ // #ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_ @@ -162,6 +162,7 @@ enum CefWrapperType { WT_TRANSLATOR_TEST_SCOPED_LIBRARY_CHILD_CHILD, WT_URLREQUEST, WT_URLREQUEST_CLIENT, + WT_UNRESPONSIVE_PROCESS_CALLBACK, WT_V8ACCESSOR, WT_V8ARRAY_BUFFER_RELEASE_CALLBACK, WT_V8CONTEXT, diff --git a/patch/patch.cfg b/patch/patch.cfg index 83378f9c8..5fa2a20ab 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -340,6 +340,11 @@ patches = [ # Add BrowserPluginGuest::owner_web_contents() method. 'name': 'chrome_plugins', }, + { + # Expose sad tab error strings. + # https://github.com/chromiumembedded/cef/issues/3661 + 'name': 'chrome_sad_tab_error', + }, { # Don't create databases, blob_storage or VideoDecodeStats directories when # cache_path is empty. diff --git a/patch/patches/chrome_browser_browser.patch b/patch/patches/chrome_browser_browser.patch index 4115a5b76..90f75c09e 100644 --- a/patch/patches/chrome_browser_browser.patch +++ b/patch/patches/chrome_browser_browser.patch @@ -132,7 +132,7 @@ index 768e11b9e5648..b4ba1800def14 100644 ] } diff --git chrome/browser/ui/browser.cc chrome/browser/ui/browser.cc -index 0791cc8f55a92..d94ac23dc878a 100644 +index 0791cc8f55a92..df3fb31c5c787 100644 --- chrome/browser/ui/browser.cc +++ chrome/browser/ui/browser.cc @@ -264,6 +264,25 @@ @@ -301,7 +301,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 void Browser::BeforeUnloadFired(WebContents* web_contents, bool proceed, bool* proceed_to_fire_unload) { -@@ -1975,6 +2060,10 @@ void Browser::WebContentsCreated(WebContents* source_contents, +@@ -1975,12 +2060,24 @@ void Browser::WebContentsCreated(WebContents* source_contents, // Make the tab show up in the task manager. task_manager::WebContentsTags::CreateForTabContents(new_contents); @@ -312,7 +312,35 @@ index 0791cc8f55a92..d94ac23dc878a 100644 } void Browser::RendererUnresponsive( -@@ -2119,11 +2208,15 @@ void Browser::EnterFullscreenModeForTab( + WebContents* source, + content::RenderWidgetHost* render_widget_host, + base::RepeatingClosure hang_monitor_restarter) { ++#if BUILDFLAG(ENABLE_CEF) ++ if (cef_browser_delegate_ && ++ cef_browser_delegate_->RendererUnresponsiveEx(source, render_widget_host, ++ hang_monitor_restarter)) { ++ return; ++ } ++#endif ++ + // Don't show the page hung dialog when a HTML popup hangs because + // the dialog will take the focus and immediately close the popup. + RenderWidgetHostView* view = render_widget_host->GetView(); +@@ -1993,6 +2090,13 @@ void Browser::RendererUnresponsive( + void Browser::RendererResponsive( + WebContents* source, + content::RenderWidgetHost* render_widget_host) { ++#if BUILDFLAG(ENABLE_CEF) ++ if (cef_browser_delegate_ && ++ cef_browser_delegate_->RendererResponsiveEx(source, render_widget_host)) { ++ return; ++ } ++#endif ++ + RenderWidgetHostView* view = render_widget_host->GetView(); + if (view && !render_widget_host->GetView()->IsHTMLFormPopup()) { + TabDialogs::FromWebContents(source)->HideHungRendererDialog( +@@ -2119,11 +2223,15 @@ void Browser::EnterFullscreenModeForTab( const blink::mojom::FullscreenOptions& options) { exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab( requesting_frame, options.display_id); @@ -328,7 +356,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 } bool Browser::IsFullscreenForTabOrPending(const WebContents* web_contents) { -@@ -2322,6 +2415,15 @@ void Browser::RequestMediaAccessPermission( +@@ -2322,6 +2430,15 @@ void Browser::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) { @@ -344,7 +372,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 const extensions::Extension* extension = GetExtensionForOrigin(profile_, request.security_origin); MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( -@@ -2858,9 +2960,11 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) { +@@ -2858,9 +2975,11 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) { // Browser, Getters for UI (private): StatusBubble* Browser::GetStatusBubble() { @@ -357,7 +385,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 } // We hide the status bar for web apps windows as this matches native -@@ -2868,6 +2972,12 @@ StatusBubble* Browser::GetStatusBubble() { +@@ -2868,6 +2987,12 @@ StatusBubble* Browser::GetStatusBubble() { // mode, as the minimal browser UI includes the status bar. if (web_app::AppBrowserController::IsWebApp(this) && !app_controller()->HasMinimalUiButtons()) { @@ -370,7 +398,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 return nullptr; } -@@ -3004,6 +3114,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) { +@@ -3004,6 +3129,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) { BookmarkTabHelper::FromWebContents(web_contents)->RemoveObserver(this); web_contents_collection_.StopObserving(web_contents); } @@ -379,7 +407,7 @@ index 0791cc8f55a92..d94ac23dc878a 100644 } void Browser::TabDetachedAtImpl(content::WebContents* contents, -@@ -3158,6 +3270,14 @@ bool Browser::PictureInPictureBrowserSupportsWindowFeature( +@@ -3158,6 +3285,14 @@ bool Browser::PictureInPictureBrowserSupportsWindowFeature( bool Browser::SupportsWindowFeatureImpl(WindowFeature feature, bool check_can_support) const { diff --git a/patch/patches/chrome_sad_tab_error.patch b/patch/patches/chrome_sad_tab_error.patch new file mode 100644 index 000000000..a5d7dbde6 --- /dev/null +++ b/patch/patches/chrome_sad_tab_error.patch @@ -0,0 +1,29 @@ +diff --git chrome/browser/ui/views/sad_tab_view.cc chrome/browser/ui/views/sad_tab_view.cc +index 26d1d804f74e7..87f411df5a49c 100644 +--- chrome/browser/ui/views/sad_tab_view.cc ++++ chrome/browser/ui/views/sad_tab_view.cc +@@ -680,6 +680,11 @@ void SadTabView::OnBoundsChanged(const gfx::Rect& previous_bounds) { + title_->SizeToFit(max_width); + } + ++// static ++std::u16string SadTabView::ErrorToString(int error_code) { ++ return ::ErrorToString(error_code); ++} ++ + SadTab* SadTab::Create(content::WebContents* web_contents, SadTabKind kind) { + return new SadTabView(web_contents, kind); + } +diff --git chrome/browser/ui/views/sad_tab_view.h chrome/browser/ui/views/sad_tab_view.h +index d6cb0e1a28eb2..59aca7bbb17c3 100644 +--- chrome/browser/ui/views/sad_tab_view.h ++++ chrome/browser/ui/views/sad_tab_view.h +@@ -56,6 +56,8 @@ class SadTabView : public SadTab, public views::View { + // Overridden from views::View: + void OnBoundsChanged(const gfx::Rect& previous_bounds) override; + ++ static std::u16string ErrorToString(int error_code); ++ + protected: + // Overridden from views::View: + void OnPaint(gfx::Canvas* canvas) override; diff --git a/tests/cefclient/browser/base_client_handler.cc b/tests/cefclient/browser/base_client_handler.cc new file mode 100644 index 000000000..9832cce1d --- /dev/null +++ b/tests/cefclient/browser/base_client_handler.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "tests/cefclient/browser/base_client_handler.h" + +namespace client { + +BaseClientHandler::BaseClientHandler() { + resource_manager_ = new CefResourceManager(); + test_runner::SetupResourceManager(resource_manager_, &string_resource_map_); +} + +// static +CefRefPtr BaseClientHandler::GetForBrowser( + CefRefPtr browser) { + return static_cast(browser->GetHost()->GetClient().get()); +} + +bool BaseClientHandler::OnProcessMessageReceived( + CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) { + CEF_REQUIRE_UI_THREAD(); + return message_router_->OnProcessMessageReceived(browser, frame, + source_process, message); +} + +void BaseClientHandler::OnAfterCreated(CefRefPtr browser) { + CEF_REQUIRE_UI_THREAD(); + + browser_count_++; + + if (!message_router_) { + // Create the browser-side router for query handling. + CefMessageRouterConfig config; + message_router_ = CefMessageRouterBrowserSide::Create(config); + + // Register handlers with the router. + test_runner::CreateMessageHandlers(message_handler_set_); + for (auto* message_handler : message_handler_set_) { + message_router_->AddHandler(message_handler, false); + } + } +} + +void BaseClientHandler::OnBeforeClose(CefRefPtr browser) { + CEF_REQUIRE_UI_THREAD(); + + if (--browser_count_ == 0) { + // Remove and delete message router handlers. + for (auto* message_handler : message_handler_set_) { + message_router_->RemoveHandler(message_handler); + delete message_handler; + } + message_handler_set_.clear(); + message_router_ = nullptr; + } +} + +bool BaseClientHandler::OnBeforeBrowse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + bool user_gesture, + bool is_redirect) { + CEF_REQUIRE_UI_THREAD(); + message_router_->OnBeforeBrowse(browser, frame); + return false; +} + +bool BaseClientHandler::OnRenderProcessUnresponsive( + CefRefPtr browser, + CefRefPtr callback) { + switch (hang_action_) { + case HangAction::kDefault: + return false; + case HangAction::kWait: + callback->Wait(); + break; + case HangAction::kTerminate: + callback->Terminate(); + break; + } + return true; +} + +void BaseClientHandler::OnRenderProcessTerminated( + CefRefPtr browser, + TerminationStatus status, + int error_code, + const CefString& error_string) { + CEF_REQUIRE_UI_THREAD(); + message_router_->OnRenderProcessTerminated(browser); +} + +cef_return_value_t BaseClientHandler::OnBeforeResourceLoad( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr callback) { + CEF_REQUIRE_IO_THREAD(); + + return resource_manager_->OnBeforeResourceLoad(browser, frame, request, + callback); +} + +CefRefPtr BaseClientHandler::GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) { + CEF_REQUIRE_IO_THREAD(); + + return resource_manager_->GetResourceHandler(browser, frame, request); +} + +CefRefPtr BaseClientHandler::GetResourceResponseFilter( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) { + CEF_REQUIRE_IO_THREAD(); + + return test_runner::GetResourceResponseFilter(browser, frame, request, + response); +} + +int BaseClientHandler::GetBrowserCount() const { + CEF_REQUIRE_UI_THREAD(); + return browser_count_; +} + +void BaseClientHandler::SetStringResource(const std::string& page, + const std::string& data) { + if (!CefCurrentlyOn(TID_IO)) { + CefPostTask(TID_IO, base::BindOnce(&BaseClientHandler::SetStringResource, + this, page, data)); + return; + } + + string_resource_map_[page] = data; +} + +void BaseClientHandler::SetHangAction(HangAction action) { + CEF_REQUIRE_UI_THREAD(); + hang_action_ = action; +} + +BaseClientHandler::HangAction BaseClientHandler::GetHangAction() const { + CEF_REQUIRE_UI_THREAD(); + return hang_action_; +} + +} // namespace client diff --git a/tests/cefclient/browser/base_client_handler.h b/tests/cefclient/browser/base_client_handler.h new file mode 100644 index 000000000..e811c03dc --- /dev/null +++ b/tests/cefclient/browser/base_client_handler.h @@ -0,0 +1,125 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFCLIENT_BROWSER_BASE_CLIENT_HANDLER_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_BASE_CLIENT_HANDLER_H_ +#pragma once + +#include "include/cef_client.h" +#include "include/wrapper/cef_message_router.h" +#include "tests/cefclient/browser/test_runner.h" + +namespace client { + +// Abstract base class for client handlers. +class BaseClientHandler : public CefClient, + public CefLifeSpanHandler, + public CefRequestHandler, + public CefResourceRequestHandler { + public: + BaseClientHandler(); + + // Returns the BaseClientHandler associated with |browser|. + static CefRefPtr GetForBrowser( + CefRefPtr browser); + + // CefClient methods + CefRefPtr GetLifeSpanHandler() override { return this; } + CefRefPtr GetRequestHandler() override { return this; } + bool OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override; + + // CefLifeSpanHandler methods + void OnAfterCreated(CefRefPtr browser) override; + void OnBeforeClose(CefRefPtr browser) override; + + // CefRequestHandler methods + bool OnBeforeBrowse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + bool user_gesture, + bool is_redirect) override; + CefRefPtr GetResourceRequestHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + bool is_navigation, + bool is_download, + const CefString& request_initiator, + bool& disable_default_handling) override { + return this; + } + bool OnRenderProcessUnresponsive( + CefRefPtr browser, + CefRefPtr callback) override; + void OnRenderProcessTerminated(CefRefPtr browser, + TerminationStatus status, + int error_code, + const CefString& error_string) override; + + // CefResourceRequestHandler methods + cef_return_value_t OnBeforeResourceLoad( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr callback) override; + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override; + CefRefPtr GetResourceResponseFilter( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override; + + // Returns the number of browsers currently using this handler. Can only be + // called on the CEF UI thread. + int GetBrowserCount() const; + + // Set a string resource for loading via StringResourceProvider. + void SetStringResource(const std::string& page, const std::string& data); + + // Action to be taken when the render process becomes unresponsive. + enum class HangAction { + kDefault, + kWait, + kTerminate, + }; + void SetHangAction(HangAction action); + HangAction GetHangAction() const; + + protected: + CefRefPtr GetResourceManager() const { + return resource_manager_; + } + + private: + // The current number of browsers using this handler. + int browser_count_ = 0; + + // Handles the browser side of query routing. The renderer side is handled + // in client_renderer.cc. + CefRefPtr message_router_; + + // Set of Handlers registered with the message router. + test_runner::MessageHandlerSet message_handler_set_; + + // Manages the registration and delivery of resources. + CefRefPtr resource_manager_; + + // Used to manage string resources in combination with StringResourceProvider. + // Only accessed on the IO thread. + test_runner::StringResourceMap string_resource_map_; + + HangAction hang_action_ = HangAction::kDefault; + + DISALLOW_COPY_AND_ASSIGN(BaseClientHandler); +}; + +} // namespace client + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_BASE_CLIENT_HANDLER_H_ diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index e950e413a..a0c9cd89f 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -168,19 +168,22 @@ std::string GetContentStatusString(cef_ssl_content_status_t status) { // Load a data: URI containing the error message. void LoadErrorPage(CefRefPtr frame, + const std::string& title, const std::string& failed_url, - cef_errorcode_t error_code, + const std::string& error_string, const std::string& other_info) { - std::stringstream ss; - ss << "Page failed to load" - "" - "

Page failed to load.

" - "URL: " << failed_url - << "
Error: " << test_runner::GetErrorString(error_code) << " (" - << error_code << ")"; + if (MainContext::Get()->UseChromeRuntime()) { + // Use default error pages with Chrome runtime. + return; + } - if (!other_info.empty()) { + std::stringstream ss; + ss << "" << title + << "

" << title + << "

URL: " << failed_url + << "
Error: " << error_string; + + if (!other_info.empty() && other_info != error_string) { ss << "
" << other_info; } @@ -463,9 +466,6 @@ ClientHandler::ClientHandler(Delegate* delegate, console_log_file_(MainContext::Get()->GetConsoleLogPath()) { DCHECK(!console_log_file_.empty()); - resource_manager_ = new CefResourceManager(); - test_runner::SetupResourceManager(resource_manager_, &string_resource_map_); - // Read command line settings. CefRefPtr command_line = CefCommandLine::GetGlobalCommandLine(); @@ -538,8 +538,8 @@ bool ClientHandler::OnProcessMessageReceived( const auto finish_time = bv_utils::Now(); - if (message_router_->OnProcessMessageReceived(browser, frame, source_process, - message)) { + if (BaseClientHandler::OnProcessMessageReceived(browser, frame, + source_process, message)) { return true; } @@ -973,21 +973,7 @@ void ClientHandler::OnBeforeDevToolsPopup( void ClientHandler::OnAfterCreated(CefRefPtr browser) { CEF_REQUIRE_UI_THREAD(); - - browser_count_++; - - if (!message_router_) { - // Create the browser-side router for query handling. - CefMessageRouterConfig config; - message_router_ = CefMessageRouterBrowserSide::Create(config); - - // Register handlers with the router. - test_runner::CreateMessageHandlers(message_handler_set_); - MessageHandlerSet::const_iterator it = message_handler_set_.begin(); - for (; it != message_handler_set_.end(); ++it) { - message_router_->AddHandler(*(it), false); - } - } + BaseClientHandler::OnAfterCreated(browser); // Set offline mode if requested via the command-line flag. if (offline_) { @@ -1002,8 +988,8 @@ void ClientHandler::OnAfterCreated(CefRefPtr browser) { CefRefPtr extension = browser->GetHost()->GetExtension(); if (extension_util::IsInternalExtension(extension->GetPath())) { // Register the internal handler for extension resources. - extension_util::AddInternalExtensionToResourceManager(extension, - resource_manager_); + extension_util::AddInternalExtensionToResourceManager( + extension, GetResourceManager()); } } @@ -1022,18 +1008,7 @@ bool ClientHandler::DoClose(CefRefPtr browser) { void ClientHandler::OnBeforeClose(CefRefPtr browser) { CEF_REQUIRE_UI_THREAD(); - - if (--browser_count_ == 0) { - // Remove and delete message router handlers. - MessageHandlerSet::const_iterator it = message_handler_set_.begin(); - for (; it != message_handler_set_.end(); ++it) { - message_router_->RemoveHandler(*(it)); - delete *(it); - } - message_handler_set_.clear(); - message_router_ = nullptr; - } - + BaseClientHandler::OnBeforeClose(browser); NotifyBrowserClosed(browser); } @@ -1072,7 +1047,8 @@ void ClientHandler::OnLoadError(CefRefPtr browser, } // Load the error page. - LoadErrorPage(frame, failedUrl, errorCode, errorText); + LoadErrorPage(frame, "Page failed to load", failedUrl, + test_runner::GetErrorString(errorCode), errorText); } bool ClientHandler::OnRequestMediaAccessPermission( @@ -1086,17 +1062,6 @@ bool ClientHandler::OnRequestMediaAccessPermission( return true; } -bool ClientHandler::OnBeforeBrowse(CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - bool user_gesture, - bool is_redirect) { - CEF_REQUIRE_UI_THREAD(); - - message_router_->OnBeforeBrowse(browser, frame); - return false; -} - bool ClientHandler::OnOpenURLFromTab( CefRefPtr browser, CefRefPtr frame, @@ -1176,7 +1141,8 @@ bool ClientHandler::OnCertificateError(CefRefPtr browser, CefRefPtr cert = ssl_info->GetX509Certificate(); if (cert.get()) { // Load the error page. - LoadErrorPage(browser->GetMainFrame(), request_url, cert_error, + LoadErrorPage(browser->GetMainFrame(), "SSL certificate error", request_url, + test_runner::GetErrorString(cert_error), GetCertificateInformation(cert, ssl_info->GetCertStatus())); } @@ -1220,10 +1186,12 @@ bool ClientHandler::OnSelectClientCertificate( } void ClientHandler::OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) { + TerminationStatus status, + int error_code, + const CefString& error_string) { CEF_REQUIRE_UI_THREAD(); - - message_router_->OnRenderProcessTerminated(browser); + BaseClientHandler::OnRenderProcessTerminated(browser, status, error_code, + error_string); // Don't reload if there's no start URL, or if the crash URL was specified. if (startup_url_.empty() || startup_url_ == "chrome://crash") { @@ -1245,6 +1213,10 @@ void ClientHandler::OnRenderProcessTerminated(CefRefPtr browser, // Don't reload the URL that just resulted in termination. if (url.find(start_url) == 0) { + LoadErrorPage(frame, "Render process terminated", frame->GetURL(), + test_runner::GetErrorString(status) + " (" + + error_string.ToString() + ")", + std::string()); return; } @@ -1262,37 +1234,6 @@ void ClientHandler::OnDocumentAvailableInMainFrame( } } -cef_return_value_t ClientHandler::OnBeforeResourceLoad( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr callback) { - CEF_REQUIRE_IO_THREAD(); - - return resource_manager_->OnBeforeResourceLoad(browser, frame, request, - callback); -} - -CefRefPtr ClientHandler::GetResourceHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request) { - CEF_REQUIRE_IO_THREAD(); - - return resource_manager_->GetResourceHandler(browser, frame, request); -} - -CefRefPtr ClientHandler::GetResourceResponseFilter( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr response) { - CEF_REQUIRE_IO_THREAD(); - - return test_runner::GetResourceResponseFilter(browser, frame, request, - response); -} - void ClientHandler::OnProtocolExecution(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, @@ -1307,11 +1248,6 @@ void ClientHandler::OnProtocolExecution(CefRefPtr browser, } } -int ClientHandler::GetBrowserCount() const { - CEF_REQUIRE_UI_THREAD(); - return browser_count_; -} - void ClientHandler::ShowDevTools(CefRefPtr browser, const CefPoint& inspect_element_at) { if (!CefCurrentlyOn(TID_UI)) { @@ -1404,17 +1340,6 @@ void ClientHandler::ShowSSLInformation(CefRefPtr browser) { std::move(config)); } -void ClientHandler::SetStringResource(const std::string& page, - const std::string& data) { - if (!CefCurrentlyOn(TID_IO)) { - CefPostTask(TID_IO, base::BindOnce(&ClientHandler::SetStringResource, this, - page, data)); - return; - } - - string_resource_map_[page] = data; -} - bool ClientHandler::CreatePopupWindow(CefRefPtr browser, bool is_devtools, const CefPopupFeatures& popupFeatures, diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index 3250ea99c..94389858e 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -11,8 +11,7 @@ #include "include/cef_client.h" #include "include/wrapper/cef_helpers.h" -#include "include/wrapper/cef_message_router.h" -#include "include/wrapper/cef_resource_manager.h" +#include "tests/cefclient/browser/base_client_handler.h" #include "tests/cefclient/browser/client_types.h" #include "tests/cefclient/browser/test_runner.h" @@ -27,7 +26,7 @@ class ClientDownloadImageCallback; // Client handler abstract base class. Provides common functionality shared by // all concrete client handler implementations. -class ClientHandler : public CefClient, +class ClientHandler : public BaseClientHandler, public CefCommandHandler, public CefContextMenuHandler, public CefDisplayHandler, @@ -35,11 +34,8 @@ class ClientHandler : public CefClient, public CefDragHandler, public CefFocusHandler, public CefKeyboardHandler, - public CefLifeSpanHandler, public CefLoadHandler, - public CefPermissionHandler, - public CefRequestHandler, - public CefResourceRequestHandler { + public CefPermissionHandler { public: // Implement this interface to receive notification of ClientHandler // events. The methods of this class will be called on the main thread unless @@ -89,8 +85,6 @@ class ClientHandler : public CefClient, virtual ~Delegate() = default; }; - typedef std::set MessageHandlerSet; - // Constructor may be called on any thread. // |delegate| must outlive this object or DetachDelegate() must be called. ClientHandler(Delegate* delegate, @@ -112,9 +106,7 @@ class ClientHandler : public CefClient, CefRefPtr GetDragHandler() override { return this; } CefRefPtr GetFocusHandler() override { return this; } CefRefPtr GetKeyboardHandler() override { return this; } - CefRefPtr GetLifeSpanHandler() override { return this; } CefRefPtr GetLoadHandler() override { return this; } - CefRefPtr GetRequestHandler() override { return this; } CefRefPtr GetPermissionHandler() override { return this; } @@ -254,11 +246,6 @@ class ClientHandler : public CefClient, CefRefPtr callback) override; // CefRequestHandler methods - bool OnBeforeBrowse(CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - bool user_gesture, - bool is_redirect) override; bool OnOpenURLFromTab( CefRefPtr browser, CefRefPtr frame, @@ -294,33 +281,17 @@ class ClientHandler : public CefClient, const X509CertificateList& certificates, CefRefPtr callback) override; void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override; + TerminationStatus status, + int error_code, + const CefString& error_string) override; void OnDocumentAvailableInMainFrame(CefRefPtr browser) override; // CefResourceRequestHandler methods - cef_return_value_t OnBeforeResourceLoad( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr callback) override; - CefRefPtr GetResourceHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request) override; - CefRefPtr GetResourceResponseFilter( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr response) override; void OnProtocolExecution(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, bool& allow_os_execution) override; - // Returns the number of browsers currently using this handler. Can only be - // called on the CEF UI thread. - int GetBrowserCount() const; - // Show a new DevTools popup window. void ShowDevTools(CefRefPtr browser, const CefPoint& inspect_element_at); @@ -334,9 +305,6 @@ class ClientHandler : public CefClient, // Show SSL information for the current site. void ShowSSLInformation(CefRefPtr browser); - // Set a string resource for loading via StringResourceProvider. - void SetStringResource(const std::string& page, const std::string& data); - // Returns the Delegate. Delegate* delegate() const { return delegate_; } @@ -416,17 +384,6 @@ class ClientHandler : public CefClient, CefRefPtr print_handler_; #endif - // Handles the browser side of query routing. The renderer side is handled - // in client_renderer.cc. - CefRefPtr message_router_; - - // Manages the registration and delivery of resources. - CefRefPtr resource_manager_; - - // Used to manage string resources in combination with StringResourceProvider. - // Only accessed on the IO thread. - test_runner::StringResourceMap string_resource_map_; - // MAIN THREAD MEMBERS // The following members will only be accessed on the main thread. This will // be the same as the CEF UI thread except when using multi-threaded message @@ -444,9 +401,6 @@ class ClientHandler : public CefClient, int radio_item = 0; } test_menu_state_; - // The current number of browsers using this handler. - int browser_count_ = 0; - // Console logging state. const std::string console_log_file_; @@ -456,9 +410,6 @@ class ClientHandler : public CefClient, // True for the initial navigation after browser creation. bool initial_navigation_ = true; - // Set of Handlers registered with the message router. - MessageHandlerSet message_handler_set_; - DISALLOW_COPY_AND_ASSIGN(ClientHandler); }; diff --git a/tests/cefclient/browser/default_client_handler.cc b/tests/cefclient/browser/default_client_handler.cc deleted file mode 100644 index d42f32a8c..000000000 --- a/tests/cefclient/browser/default_client_handler.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2023 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#include "tests/cefclient/browser/default_client_handler.h" - -#include "tests/cefclient/browser/test_runner.h" - -namespace client { - -DefaultClientHandler::DefaultClientHandler() { - resource_manager_ = new CefResourceManager(); - test_runner::SetupResourceManager(resource_manager_, nullptr); -} - -CefRefPtr -DefaultClientHandler::GetResourceRequestHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - bool is_navigation, - bool is_download, - const CefString& request_initiator, - bool& disable_default_handling) { - CEF_REQUIRE_IO_THREAD(); - return this; -} - -cef_return_value_t DefaultClientHandler::OnBeforeResourceLoad( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr callback) { - CEF_REQUIRE_IO_THREAD(); - - return resource_manager_->OnBeforeResourceLoad(browser, frame, request, - callback); -} - -CefRefPtr DefaultClientHandler::GetResourceHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request) { - CEF_REQUIRE_IO_THREAD(); - - return resource_manager_->GetResourceHandler(browser, frame, request); -} - -CefRefPtr DefaultClientHandler::GetResourceResponseFilter( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr response) { - CEF_REQUIRE_IO_THREAD(); - - return test_runner::GetResourceResponseFilter(browser, frame, request, - response); -} - -} // namespace client diff --git a/tests/cefclient/browser/default_client_handler.h b/tests/cefclient/browser/default_client_handler.h index 6970d4cef..529c3654f 100644 --- a/tests/cefclient/browser/default_client_handler.h +++ b/tests/cefclient/browser/default_client_handler.h @@ -6,52 +6,17 @@ #define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_ #pragma once -#include "include/cef_client.h" -#include "include/wrapper/cef_resource_manager.h" +#include "tests/cefclient/browser/base_client_handler.h" namespace client { // Default client handler for unmanaged browser windows. Used with the Chrome // runtime only. -class DefaultClientHandler : public CefClient, - public CefRequestHandler, - public CefResourceRequestHandler { +class DefaultClientHandler : public BaseClientHandler { public: - DefaultClientHandler(); - - // CefClient methods - CefRefPtr GetRequestHandler() override { return this; } - - // CefRequestHandler methods - CefRefPtr GetResourceRequestHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - bool is_navigation, - bool is_download, - const CefString& request_initiator, - bool& disable_default_handling) override; - - // CefResourceRequestHandler methods - cef_return_value_t OnBeforeResourceLoad( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr callback) override; - CefRefPtr GetResourceHandler( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request) override; - CefRefPtr GetResourceResponseFilter( - CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - CefRefPtr response) override; + DefaultClientHandler() = default; private: - // Manages the registration and delivery of resources. - CefRefPtr resource_manager_; - IMPLEMENT_REFCOUNTING(DefaultClientHandler); DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler); }; diff --git a/tests/cefclient/browser/hang_test.cc b/tests/cefclient/browser/hang_test.cc new file mode 100644 index 000000000..1c3f2d5f0 --- /dev/null +++ b/tests/cefclient/browser/hang_test.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "tests/cefclient/browser/hang_test.h" + +#include + +#include "tests/cefclient/browser/base_client_handler.h" +#include "tests/cefclient/browser/test_runner.h" + +namespace client::hang_test { + +namespace { + +const char kTestUrlPath[] = "/hang"; +const char kTestMessageName[] = "HangTest"; + +// Handle messages in the browser process. +class Handler : public CefMessageRouterBrowserSide::Handler { + public: + Handler() = default; + + // Called due to cefQuery execution in hang.html. + bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64_t query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) override { + // Only handle messages from the test URL. + const std::string& url = frame->GetURL(); + if (!test_runner::IsTestURL(url, kTestUrlPath)) { + return false; + } + + if (auto client_handler = BaseClientHandler::GetForBrowser(browser)) { + using HangAction = BaseClientHandler::HangAction; + + const std::string& message_name = request; + if (message_name.find(kTestMessageName) == 0) { + const std::string& command = + message_name.substr(sizeof(kTestMessageName)); + if (command == "getcommand") { + std::string current; + switch (client_handler->GetHangAction()) { + case HangAction::kDefault: + current = "default"; + break; + case HangAction::kWait: + current = "wait"; + break; + case HangAction::kTerminate: + current = "terminate"; + break; + } + callback->Success(current); + } else if (command == "setdefault") { + client_handler->SetHangAction(HangAction::kDefault); + } else if (command == "setwait") { + client_handler->SetHangAction(HangAction::kWait); + } else if (command == "setterminate") { + client_handler->SetHangAction(HangAction::kTerminate); + } else { + LOG(ERROR) << "Unrecognized command: " << command; + } + return true; + } + } + + return false; + } +}; + +} // namespace + +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) { + handlers.insert(new Handler()); +} + +} // namespace client::hang_test diff --git a/tests/cefclient/browser/hang_test.h b/tests/cefclient/browser/hang_test.h new file mode 100644 index 000000000..f891a4364 --- /dev/null +++ b/tests/cefclient/browser/hang_test.h @@ -0,0 +1,18 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFCLIENT_BROWSER_HANG_TEST_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_HANG_TEST_H_ +#pragma once + +#include "tests/cefclient/browser/test_runner.h" + +namespace client::hang_test { + +// Create message handlers. Called from test_runner.cc. +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers); + +} // namespace client::hang_test + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_HANG_TEST_H_ diff --git a/tests/cefclient/browser/resource.h b/tests/cefclient/browser/resource.h index 32caf8897..63031cbce 100644 --- a/tests/cefclient/browser/resource.h +++ b/tests/cefclient/browser/resource.h @@ -46,28 +46,29 @@ #define IDS_BINDING_HTML 1001 #define IDS_DIALOGS_HTML 1002 #define IDS_DRAGGABLE_HTML 1003 -#define IDS_IPC_PERFORMANCE_HTML 1004 -#define IDS_LOCALSTORAGE_HTML 1005 -#define IDS_LOGO_PNG 1006 -#define IDS_MEDIA_ROUTER_HTML 1007 -#define IDS_MENU_ICON_1X_PNG 1008 -#define IDS_MENU_ICON_2X_PNG 1009 -#define IDS_OSRTEST_HTML 1010 -#define IDS_OTHER_TESTS_HTML 1011 -#define IDS_PDF_HTML 1012 -#define IDS_PDF_PDF 1013 -#define IDS_PERFORMANCE_HTML 1014 -#define IDS_PERFORMANCE2_HTML 1015 -#define IDS_PREFERENCES_HTML 1016 -#define IDS_RESPONSE_FILTER_HTML 1017 -#define IDS_SERVER_HTML 1018 -#define IDS_TRANSPARENCY_HTML 1019 -#define IDS_URLREQUEST_HTML 1020 -#define IDS_WEBSOCKET_HTML 1021 -#define IDS_WINDOW_HTML 1022 -#define IDS_WINDOW_ICON_1X_PNG 1023 -#define IDS_WINDOW_ICON_2X_PNG 1024 -#define IDS_XMLHTTPREQUEST_HTML 1025 +#define IDS_HANG_HTML 1004 +#define IDS_IPC_PERFORMANCE_HTML 1005 +#define IDS_LOCALSTORAGE_HTML 1006 +#define IDS_LOGO_PNG 1007 +#define IDS_MEDIA_ROUTER_HTML 1008 +#define IDS_MENU_ICON_1X_PNG 1009 +#define IDS_MENU_ICON_2X_PNG 1010 +#define IDS_OSRTEST_HTML 1011 +#define IDS_OTHER_TESTS_HTML 1012 +#define IDS_PDF_HTML 1013 +#define IDS_PDF_PDF 1014 +#define IDS_PERFORMANCE_HTML 1015 +#define IDS_PERFORMANCE2_HTML 1016 +#define IDS_PREFERENCES_HTML 1017 +#define IDS_RESPONSE_FILTER_HTML 1018 +#define IDS_SERVER_HTML 1019 +#define IDS_TRANSPARENCY_HTML 1020 +#define IDS_URLREQUEST_HTML 1021 +#define IDS_WEBSOCKET_HTML 1022 +#define IDS_WINDOW_HTML 1023 +#define IDS_WINDOW_ICON_1X_PNG 1024 +#define IDS_WINDOW_ICON_2X_PNG 1025 +#define IDS_XMLHTTPREQUEST_HTML 1026 #define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030 #define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031 diff --git a/tests/cefclient/browser/resource_util_win_idmap.cc b/tests/cefclient/browser/resource_util_win_idmap.cc index 1757e68de..09488edba 100644 --- a/tests/cefclient/browser/resource_util_win_idmap.cc +++ b/tests/cefclient/browser/resource_util_win_idmap.cc @@ -25,6 +25,7 @@ int GetResourceId(const char* resource_name) { IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML}, {"extensions/set_page_color/popup.js", IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS}, + {"hang.html", IDS_HANG_HTML}, {"ipc_performance.html", IDS_IPC_PERFORMANCE_HTML}, {"localstorage.html", IDS_LOCALSTORAGE_HTML}, {"logo.png", IDS_LOGO_PNG}, diff --git a/tests/cefclient/browser/test_runner.cc b/tests/cefclient/browser/test_runner.cc index 0b5611144..f6fd77ce3 100644 --- a/tests/cefclient/browser/test_runner.cc +++ b/tests/cefclient/browser/test_runner.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "include/base/cef_callback.h" #include "include/cef_parser.h" @@ -16,10 +17,11 @@ #include "include/views/cef_browser_view.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_stream_resource_handler.h" +#include "tests/cefclient/browser/base_client_handler.h" #include "tests/cefclient/browser/binary_transfer_test.h" #include "tests/cefclient/browser/binding_test.h" -#include "tests/cefclient/browser/client_handler.h" #include "tests/cefclient/browser/dialog_test.h" +#include "tests/cefclient/browser/hang_test.h" #include "tests/cefclient/browser/main_context.h" #include "tests/cefclient/browser/media_router_test.h" #include "tests/cefclient/browser/preferences_test.h" @@ -50,9 +52,7 @@ const char kTestGetTextPage[] = "get_text.html"; void LoadStringResourcePage(CefRefPtr browser, const std::string& page, const std::string& data) { - CefRefPtr client = browser->GetHost()->GetClient(); - ClientHandler* client_handler = static_cast(client.get()); - client_handler->SetStringResource(page, data); + BaseClientHandler::GetForBrowser(browser)->SetStringResource(page, data); browser->GetMainFrame()->LoadURL(kTestOrigin + page); } @@ -752,10 +752,29 @@ std::string GetErrorString(cef_errorcode_t code) { CASE(ERR_CACHE_MISS); CASE(ERR_INSECURE_RESPONSE); default: - return "UNKNOWN"; + return std::to_string(static_cast(code)); } } +std::string GetErrorString(cef_termination_status_t status) { + switch (status) { + case TS_ABNORMAL_TERMINATION: + return "ABNORMAL_TERMINATION"; + case TS_PROCESS_WAS_KILLED: + return "PROCESS_WAS_KILLED"; + case TS_PROCESS_CRASHED: + return "PROCESS_CRASHED"; + case TS_PROCESS_OOM: + return "PROCESS_OOM"; + case TS_LAUNCH_FAILED: + return "LAUNCH_FAILED"; + case TS_INTEGRITY_FAILURE: + return "INTEGRITY_FAILURE"; + } + NOTREACHED(); + return std::string(); +} + void SetupResourceManager(CefRefPtr resource_manager, StringResourceMap* string_resource_map) { if (!CefCurrentlyOn(TID_IO)) { @@ -851,6 +870,9 @@ void CreateMessageHandlers(MessageHandlerSet& handlers) { // Create the dialog test handlers. dialog_test::CreateMessageHandlers(handlers); + // Create the hang test handlers. + hang_test::CreateMessageHandlers(handlers); + // Create the media router test handlers. media_router_test::CreateMessageHandlers(handlers); diff --git a/tests/cefclient/browser/test_runner.h b/tests/cefclient/browser/test_runner.h index 8de323b51..dbcac31c6 100644 --- a/tests/cefclient/browser/test_runner.h +++ b/tests/cefclient/browser/test_runner.h @@ -33,8 +33,9 @@ std::string GetDataURI(const std::string& data, const std::string& mime_type); // Returns the string representation of the specified error code. std::string GetErrorString(cef_errorcode_t code); +std::string GetErrorString(cef_termination_status_t status); -typedef std::map StringResourceMap; +using StringResourceMap = std::map; // Set up the resource manager for tests. void SetupResourceManager(CefRefPtr resource_manager, @@ -52,7 +53,7 @@ bool IsTestURL(const std::string& url, const std::string& path); // Create all CefMessageRouterBrowserSide::Handler objects. They will be // deleted when the ClientHandler is destroyed. -typedef std::set MessageHandlerSet; +using MessageHandlerSet = std::set; void CreateMessageHandlers(MessageHandlerSet& handlers); // Register scheme handlers for tests. diff --git a/tests/cefclient/cefclient_gtk.cc b/tests/cefclient/cefclient_gtk.cc index e0d170af6..e88e7e207 100644 --- a/tests/cefclient/cefclient_gtk.cc +++ b/tests/cefclient/cefclient_gtk.cc @@ -110,7 +110,7 @@ int RunMain(int argc, char* argv[]) { // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!context->Initialize(main_args, settings, app, nullptr)) { - return 1; + return CefGetExitCode(); } // Force Gtk to use Xwayland (in case a Wayland compositor is being used). diff --git a/tests/cefclient/cefclient_mac.mm b/tests/cefclient/cefclient_mac.mm index 02280d8d8..959acaaad 100644 --- a/tests/cefclient/cefclient_mac.mm +++ b/tests/cefclient/cefclient_mac.mm @@ -583,7 +583,7 @@ int RunMain(int argc, char* argv[]) { // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!context->Initialize(main_args, settings, app, nullptr)) { - return 1; + return CefGetExitCode(); } // Register scheme handlers. diff --git a/tests/cefclient/cefclient_win.cc b/tests/cefclient/cefclient_win.cc index 68fef527f..252dc2849 100644 --- a/tests/cefclient/cefclient_win.cc +++ b/tests/cefclient/cefclient_win.cc @@ -99,7 +99,7 @@ int RunMain(HINSTANCE hInstance, int nCmdShow) { // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!context->Initialize(main_args, settings, app, sandbox_info)) { - return 1; + return CefGetExitCode(); } // Register scheme handlers. diff --git a/tests/cefclient/resources/dialogs.html b/tests/cefclient/resources/dialogs.html index 537011a13..c38d35a0a 100644 --- a/tests/cefclient/resources/dialogs.html +++ b/tests/cefclient/resources/dialogs.html @@ -1,6 +1,25 @@ Dialog Test + + + +
+ +
Use the below controls to trigger a render process hang.
+
+
Hang for seconds.
+
+
Action after hanging for at least 15 seconds:
+
+
+
+
+
+
+
+
+
[1] The "Page unresponsive" dialog will be auto-dismissed when the hang ends.
+
[2] After termination the browser navigates to the startup URL or shows an error page.
+
+
+
+Observe page responsiveness: +

+ + + + +
+CSS:
+
  +JavaScript:
+
+(JavaScript will stop updating during the hang) + + diff --git a/tests/cefclient/resources/other_tests.html b/tests/cefclient/resources/other_tests.html index 7c094c236..260d75f20 100644 --- a/tests/cefclient/resources/other_tests.html +++ b/tests/cefclient/resources/other_tests.html @@ -41,6 +41,7 @@
  • XMLHttpRequest
  • Print this page with "javascript:window.print();"
  • Touch Feature Tests - requires "touch-events=enabled" flag (and CAPS LOCK on Mac for Trackpad simulation)
  • +
  • Render process hang test
  • diff --git a/tests/cefclient/resources/win/cefclient.rc b/tests/cefclient/resources/win/cefclient.rc index 2273bc0fa..20080d635 100644 --- a/tests/cefclient/resources/win/cefclient.rc +++ b/tests/cefclient/resources/win/cefclient.rc @@ -33,6 +33,7 @@ IDS_BINARY_TRANSFER_HTML BINARY "..\\binary_transfer.html" IDS_BINDING_HTML BINARY "..\\binding.html" IDS_DIALOGS_HTML BINARY "..\\dialogs.html" IDS_DRAGGABLE_HTML BINARY "..\\draggable.html" +IDS_HANG_HTML BINARY "..\\hang.html" IDS_IPC_PERFORMANCE_HTML BINARY "..\\ipc_performance.html" IDS_LOCALSTORAGE_HTML BINARY "..\\localstorage.html" IDS_LOGO_PNG BINARY "..\\logo.png" diff --git a/tests/cefsimple/cefsimple_linux.cc b/tests/cefsimple/cefsimple_linux.cc index 10a784d24..ca09f25e2 100644 --- a/tests/cefsimple/cefsimple_linux.cc +++ b/tests/cefsimple/cefsimple_linux.cc @@ -82,7 +82,7 @@ int main(int argc, char* argv[]) { // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!CefInitialize(main_args, settings, app.get(), nullptr)) { - return 1; + return CefGetExitCode(); } // Run the CEF message loop. This will block until CefQuitMessageLoop() is diff --git a/tests/cefsimple/cefsimple_mac.mm b/tests/cefsimple/cefsimple_mac.mm index ac5afe79a..4f2233e1c 100644 --- a/tests/cefsimple/cefsimple_mac.mm +++ b/tests/cefsimple/cefsimple_mac.mm @@ -174,7 +174,7 @@ int main(int argc, char* argv[]) { // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!CefInitialize(main_args, settings, app.get(), nullptr)) { - return 1; + return CefGetExitCode(); } // Create the application delegate. diff --git a/tests/cefsimple/cefsimple_win.cc b/tests/cefsimple/cefsimple_win.cc index 68119382d..f036626e3 100644 --- a/tests/cefsimple/cefsimple_win.cc +++ b/tests/cefsimple/cefsimple_win.cc @@ -95,7 +95,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, // fails or if early exit is desired (for example, due to process singleton // relaunch behavior). if (!CefInitialize(main_args, settings, app.get(), sandbox_info)) { - return 1; + return CefGetExitCode(); } // Run the CEF message loop. This will block until CefQuitMessageLoop() is diff --git a/tests/ceftests/message_router_unittest_utils.cc b/tests/ceftests/message_router_unittest_utils.cc index aff41839c..52b060272 100644 --- a/tests/ceftests/message_router_unittest_utils.cc +++ b/tests/ceftests/message_router_unittest_utils.cc @@ -184,7 +184,9 @@ void MRTestHandler::OnBeforeClose(CefRefPtr browser) { } void MRTestHandler::OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) { + TerminationStatus status, + int error_code, + const CefString& error_string) { message_router_->OnRenderProcessTerminated(browser); } diff --git a/tests/ceftests/message_router_unittest_utils.h b/tests/ceftests/message_router_unittest_utils.h index 25d57598d..96a776abc 100644 --- a/tests/ceftests/message_router_unittest_utils.h +++ b/tests/ceftests/message_router_unittest_utils.h @@ -76,7 +76,9 @@ class MRTestHandler : public TestHandler { void OnAfterCreated(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override; + TerminationStatus status, + int error_code, + const CefString& error_string) override; // Only call this method if the navigation isn't canceled. bool OnBeforeBrowse(CefRefPtr browser, diff --git a/tests/ceftests/request_handler_unittest.cc b/tests/ceftests/request_handler_unittest.cc index d1431d1cc..569e05f4c 100644 --- a/tests/ceftests/request_handler_unittest.cc +++ b/tests/ceftests/request_handler_unittest.cc @@ -259,13 +259,16 @@ class NetNotifyTestHandler : public TestHandler { } void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override { + TerminationStatus status, + int error_code, + const CefString& error_string) override { got_process_terminated_ct_++; // Termination is expected for cross-origin requests initiated from the // renderer process. if (!(test_type_ == NNTT_DELAYED_RENDERER && !same_origin_)) { - TestHandler::OnRenderProcessTerminated(browser, status); + TestHandler::OnRenderProcessTerminated(browser, status, error_code, + error_string); } FinishTest(); diff --git a/tests/ceftests/routing_test_handler.cc b/tests/ceftests/routing_test_handler.cc index d85e3e7df..758e74c9e 100644 --- a/tests/ceftests/routing_test_handler.cc +++ b/tests/ceftests/routing_test_handler.cc @@ -78,7 +78,9 @@ void RoutingTestHandler::OnBeforeClose(CefRefPtr browser) { void RoutingTestHandler::OnRenderProcessTerminated( CefRefPtr browser, - TerminationStatus status) { + TerminationStatus status, + int error_code, + const CefString& error_string) { message_router_->OnRenderProcessTerminated(browser); } diff --git a/tests/ceftests/routing_test_handler.h b/tests/ceftests/routing_test_handler.h index 4c48266b0..8f95a99dd 100644 --- a/tests/ceftests/routing_test_handler.h +++ b/tests/ceftests/routing_test_handler.h @@ -20,7 +20,9 @@ class RoutingTestHandler : public TestHandler, void OnAfterCreated(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override; + TerminationStatus status, + int error_code, + const CefString& error_string) override; // Only call this method if the navigation isn't canceled. bool OnBeforeBrowse(CefRefPtr browser, diff --git a/tests/ceftests/test_handler.cc b/tests/ceftests/test_handler.cc index 125f7aec1..19a028114 100644 --- a/tests/ceftests/test_handler.cc +++ b/tests/ceftests/test_handler.cc @@ -437,8 +437,11 @@ CefRefPtr TestHandler::GetResourceHandler( } void TestHandler::OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) { - LOG(WARNING) << "OnRenderProcessTerminated: status = " << status << "."; + TerminationStatus status, + int error_code, + const CefString& error_string) { + LOG(WARNING) << "OnRenderProcessTerminated: status = " << status + << ", error = " << error_string.ToString() << "."; } CefRefPtr TestHandler::GetBrowser() const { diff --git a/tests/ceftests/test_handler.h b/tests/ceftests/test_handler.h index 8b801e377..db5041cf6 100644 --- a/tests/ceftests/test_handler.h +++ b/tests/ceftests/test_handler.h @@ -180,7 +180,9 @@ class TestHandler : public CefClient, CefRefPtr request) override; void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status) override; + TerminationStatus status, + int error_code, + const CefString& error_string) override; // These methods should only be used if at most one non-popup browser exists. CefRefPtr GetBrowser() const;