chrome: Support unload handlers with TryCloseBrowser

TryCloseBrowser should potentially trigger JavaScript unload handlers
and return false. Add CefBrowserHost::IsReadyToBeClosed for detecting
mandatory close state. Enable, for Chrome style browsers, LifeSpanTest
that don't require DoClose(). Update related documentation.
This commit is contained in:
Marshall Greenblatt
2024-10-17 11:05:30 -04:00
parent 265a733807
commit 6e05c14db2
14 changed files with 347 additions and 121 deletions

View File

@@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=7c786570b1c7af912a31c6f0c3d742e8aeb38fd8$
// $hash=e9f34d90eb4af614e35cbb29da0639b62acec7fd$
//
#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
@@ -301,29 +301,62 @@ typedef struct _cef_browser_host_t {
struct _cef_browser_host_t* self);
///
/// Request that the browser close. The JavaScript 'onbeforeunload' event will
/// be fired. If |force_close| is false (0) the event handler, if any, will be
/// allowed to prompt the user and the user can optionally cancel the close.
/// If |force_close| is true (1) the prompt will not be displayed and the
/// close will proceed. Results in a call to
/// cef_life_span_handler_t::do_close() if the event handler allows the close
/// or if |force_close| is true (1). See cef_life_span_handler_t::do_close()
/// documentation for additional usage information.
/// Request that the browser close. Closing a browser is a multi-stage process
/// that may complete either synchronously or asynchronously, and involves
/// callbacks such as cef_life_span_handler_t::DoClose (Alloy style only),
/// cef_life_span_handler_t::OnBeforeClose, and a top-level window close
/// handler such as cef_window_delegate_t::CanClose (or platform-specific
/// equivalent). In some cases a close request may be delayed or canceled by
/// the user. Using try_close_browser() instead of close_browser() is
/// recommended for most use cases. See cef_life_span_handler_t::do_close()
/// documentation for detailed usage and examples.
///
/// If |force_close| is false (0) then JavaScript unload handlers, if any, may
/// be fired and the close may be delayed or canceled by the user. If
/// |force_close| is true (1) then the user will not be prompted and the close
/// will proceed immediately (possibly asynchronously). If browser close is
/// delayed and not canceled the default behavior is to call the top-level
/// window close handler once the browser is ready to be closed. This default
/// behavior can be changed for Alloy style browsers by implementing
/// cef_life_span_handler_t::do_close(). is_ready_to_be_closed() can be used
/// to detect mandatory browser close events when customizing close behavior
/// on the browser process UI thread.
///
void(CEF_CALLBACK* close_browser)(struct _cef_browser_host_t* self,
int force_close);
///
/// Helper for closing a browser. Call this function from the top-level window
/// close handler (if any). Internally this calls CloseBrowser(false (0)) if
/// the close has not yet been initiated. This function returns false (0)
/// while the close is pending and true (1) after the close has completed. See
/// close_browser() and cef_life_span_handler_t::do_close() documentation for
/// additional usage information. This function must be called on the browser
/// process UI thread.
/// Helper for closing a browser. This is similar in behavior to
/// CLoseBrowser(false (0)) but returns a boolean to reflect the immediate
/// close status. Call this function from a top-level window close handler
/// such as cef_window_delegate_t::CanClose (or platform-specific equivalent)
/// to request that the browser close, and return the result to indicate if
/// the window close should proceed. Returns false (0) if the close will be
/// delayed (JavaScript unload handlers triggered but still pending) or true
/// (1) if the close will proceed immediately (possibly asynchronously). See
/// close_browser() documentation for additional usage information. This
/// function must be called on the browser process UI thread.
///
int(CEF_CALLBACK* try_close_browser)(struct _cef_browser_host_t* self);
///
/// Returns true (1) if the browser is ready to be closed, meaning that the
/// close has already been initiated and that JavaScript unload handlers have
/// already executed or should be ignored. This can be used from a top-level
/// window close handler such as cef_window_delegate_t::CanClose (or platform-
/// specific equivalent) to distringuish between potentially cancelable
/// browser close events (like the user clicking the top-level window close
/// button before browser close has started) and mandatory browser close
/// events (like JavaScript `window.close()` or after browser close has
/// started in response to [Try]close_browser()). Not completing the browser
/// close for mandatory close events (when this function returns true (1))
/// will leave the browser in a partially closed state that interferes with
/// proper functioning. See close_browser() documentation for additional usage
/// information. This function must be called on the browser process UI
/// thread.
///
int(CEF_CALLBACK* is_ready_to_be_closed)(struct _cef_browser_host_t* self);
///
/// Set whether the browser is focused.
///

View File

@@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=5232dd6bf16af9b6d195a47bb41de0dfb880a65e$
// $hash=6aad2ccf30a6c519bbeee64d83866e82a41a48d8$
//
#ifndef CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_
@@ -138,35 +138,44 @@ typedef struct _cef_life_span_handler_t {
struct _cef_browser_t* browser);
///
/// Called when a browser has received a request to close. This may result
/// directly from a call to cef_browser_host_t::*close_browser() or indirectly
/// if the browser is parented to a top-level window created by CEF and the
/// user attempts to close that window (by clicking the 'X', for example). The
/// do_close() function will be called after the JavaScript 'onunload' event
/// has been fired.
/// Called when an Alloy style browser is ready to be closed, meaning that the
/// close has already been initiated and that JavaScript unload handlers have
/// already executed or should be ignored. This may result directly from a
/// call to cef_browser_host_t::[Try]close_browser() or indirectly if the
/// browser's top-level parent window was created by CEF and the user attempts
/// to close that window (by clicking the 'X', for example). do_close() will
/// not be called if the browser's host window/view has already been destroyed
/// (via parent window/view hierarchy tear-down, for example), as it is no
/// longer possible to customize the close behavior at that point.
///
/// An application should handle top-level owner window close notifications by
/// calling cef_browser_host_t::try_close_browser() or
/// An application should handle top-level parent window close notifications
/// by calling cef_browser_host_t::try_close_browser() or
/// cef_browser_host_t::CloseBrowser(false (0)) instead of allowing the window
/// to close immediately (see the examples below). This gives CEF an
/// opportunity to process the 'onbeforeunload' event and optionally cancel
/// opportunity to process JavaScript unload handlers and optionally cancel
/// the close before do_close() is called.
///
/// When windowed rendering is enabled CEF will internally create a window or
/// view to host the browser. In that case returning false (0) from do_close()
/// will send the standard close notification to the browser's top-level owner
/// window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on
/// Linux or cef_window_delegate_t::can_close() callback from Views). If the
/// browser's host window/view has already been destroyed (via view hierarchy
/// tear-down, for example) then do_close() will not be called for that
/// browser since is no longer possible to cancel the close.
/// When windowed rendering is enabled CEF will create an internal child
/// window/view to host the browser. In that case returning false (0) from
/// do_close() will send the standard close notification to the browser's top-
/// level parent window (e.g. WM_CLOSE on Windows, performClose: on OS X,
/// "delete_event" on Linux or cef_window_delegate_t::can_close() callback
/// from Views).
///
/// When windowed rendering is disabled returning false (0) from do_close()
/// will cause the browser object to be destroyed immediately.
/// When windowed rendering is disabled there is no internal window/view and
/// returning false (0) from do_close() will cause the browser object to be
/// destroyed immediately.
///
/// If the browser's top-level owner window requires a non-standard close
/// If the browser's top-level parent window requires a non-standard close
/// notification then send that notification from do_close() and return true
/// (1).
/// (1). You are still required to complete the browser close as soon as
/// possible (either by calling [Try]close_browser() or by proceeding with
/// window/view hierarchy tear-down), otherwise the browser will be left in a
/// partially closed state that interferes with proper functioning. Top-level
/// windows created on the browser process UI thread can alternately call
/// cef_browser_host_t::is_ready_to_be_closed() in the close handler to check
/// close status instead of relying on custom do_close() handling. See
/// documentation on that function for additional details.
///
/// The cef_life_span_handler_t::on_before_close() function will be called
/// after do_close() (if do_close() is called) and immediately before the
@@ -182,22 +191,26 @@ typedef struct _cef_life_span_handler_t {
/// which sends a close notification
/// to the application's top-level window.
/// 2. Application's top-level window receives the close notification and
/// calls TryCloseBrowser() (which internally calls CloseBrowser(false)).
/// calls TryCloseBrowser() (similar to calling CloseBrowser(false)).
/// TryCloseBrowser() returns false so the client cancels the window
/// close.
/// 3. JavaScript 'onbeforeunload' handler executes and shows the close
/// confirmation dialog (which can be overridden via
/// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close. 5. JavaScript 'onunload' handler executes.
/// 6. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false by default).
/// 7. Application's top-level window receives the close notification and
/// 6. Application's do_close() handler is called and returns false (0) by
/// default.
/// 7. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false).
/// 8. Application's top-level window receives the close notification and
/// calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
/// allows the window close.
/// 8. Application's top-level window is destroyed. 9. Application's
/// on_before_close() handler is called and the browser object
/// 9. Application's top-level window is destroyed, triggering destruction
/// of the child browser window.
/// 10. Application's on_before_close() handler is called and the browser
/// object
/// is destroyed.
/// 10. Application exits by calling cef_quit_message_loop() if no other
/// 11. Application exits by calling cef_quit_message_loop() if no other
/// browsers
/// exist.
///
@@ -215,13 +228,17 @@ typedef struct _cef_life_span_handler_t {
/// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close. 5. JavaScript 'onunload' handler executes.
/// 6. Application's do_close() handler is called. Application will:
/// A. Set a flag to indicate that the next close attempt will be allowed.
/// A. Set a flag to indicate that the next top-level window close attempt
/// will be allowed.
/// B. Return false.
/// 7. CEF sends an close notification to the application's top-level window.
/// 7. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false).
/// 8. Application's top-level window receives the close notification and
/// allows the window to close based on the flag from #6B.
/// 9. Application's top-level window is destroyed. 10. Application's
/// on_before_close() handler is called and the browser object
/// allows the window to close based on the flag from #6A.
/// 9. Application's top-level window is destroyed, triggering destruction
/// of the child browser window.
/// 10. Application's on_before_close() handler is called and the browser
/// object
/// is destroyed.
/// 11. Application exits by calling cef_quit_message_loop() if no other
/// browsers

View File

@@ -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 "9717d7221d63adfd79ee52e2a31c9ac7acdd6d50"
#define CEF_API_HASH_UNIVERSAL "676af077d6826353caf40425f5f2bae1262347ea"
#if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "072a4fe61a512f21fd0d664495902fca6ec2193b"
#define CEF_API_HASH_PLATFORM "51848171cdea10858c4e0fca0f7099b0fdc759f9"
#elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "ee7f0e9247add8df0827d02da32559e38e8079d0"
#define CEF_API_HASH_PLATFORM "8cc826c5f5fe97c275dfa3b9c020470678a5d2fd"
#elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "88996e58ee062016efd054bfbafd03dd3daa99ed"
#define CEF_API_HASH_PLATFORM "0aec2348de1bf14fafa7a23baa0df942d342d409"
#endif
#ifdef __cplusplus

View File

@@ -331,30 +331,63 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
///
/// Request that the browser close. The JavaScript 'onbeforeunload' event will
/// be fired. If |force_close| is false the event handler, if any, will be
/// allowed to prompt the user and the user can optionally cancel the close.
/// If |force_close| is true the prompt will not be displayed and the close
/// will proceed. Results in a call to CefLifeSpanHandler::DoClose() if the
/// event handler allows the close or if |force_close| is true. See
/// CefLifeSpanHandler::DoClose() documentation for additional usage
/// information.
/// Request that the browser close. Closing a browser is a multi-stage process
/// that may complete either synchronously or asynchronously, and involves
/// callbacks such as CefLifeSpanHandler::DoClose (Alloy style only),
/// CefLifeSpanHandler::OnBeforeClose, and a top-level window close handler
/// such as CefWindowDelegate::CanClose (or platform-specific equivalent). In
/// some cases a close request may be delayed or canceled by the user. Using
/// TryCloseBrowser() instead of CloseBrowser() is recommended for most use
/// cases. See CefLifeSpanHandler::DoClose() documentation for detailed usage
/// and examples.
///
/// If |force_close| is false then JavaScript unload handlers, if any, may be
/// fired and the close may be delayed or canceled by the user. If
/// |force_close| is true then the user will not be prompted and the close
/// will proceed immediately (possibly asynchronously). If browser close is
/// delayed and not canceled the default behavior is to call the top-level
/// window close handler once the browser is ready to be closed. This default
/// behavior can be changed for Alloy style browsers by implementing
/// CefLifeSpanHandler::DoClose(). IsReadyToBeClosed() can be used to detect
/// mandatory browser close events when customizing close behavior on the
/// browser process UI thread.
///
/*--cef()--*/
virtual void CloseBrowser(bool force_close) = 0;
///
/// Helper for closing a browser. Call this method from the top-level window
/// close handler (if any). Internally this calls CloseBrowser(false) if the
/// close has not yet been initiated. This method returns false while the
/// close is pending and true after the close has completed. See
/// CloseBrowser() and CefLifeSpanHandler::DoClose() documentation for
/// additional usage information. This method must be called on the browser
/// process UI thread.
/// Helper for closing a browser. This is similar in behavior to
/// CLoseBrowser(false) but returns a boolean to reflect the immediate close
/// status. Call this method from a top-level window close handler such as
/// CefWindowDelegate::CanClose (or platform-specific equivalent) to request
/// that the browser close, and return the result to indicate if the window
/// close should proceed. Returns false if the close will be delayed
/// (JavaScript unload handlers triggered but still pending) or true if the
/// close will proceed immediately (possibly asynchronously). See
/// CloseBrowser() documentation for additional usage information. This method
/// must be called on the browser process UI thread.
///
/*--cef()--*/
virtual bool TryCloseBrowser() = 0;
///
/// Returns true if the browser is ready to be closed, meaning that the close
/// has already been initiated and that JavaScript unload handlers have
/// already executed or should be ignored. This can be used from a top-level
/// window close handler such as CefWindowDelegate::CanClose (or
/// platform-specific equivalent) to distringuish between potentially
/// cancelable browser close events (like the user clicking the top-level
/// window close button before browser close has started) and mandatory
/// browser close events (like JavaScript `window.close()` or after browser
/// close has started in response to [Try]CloseBrowser()). Not completing the
/// browser close for mandatory close events (when this method returns true)
/// will leave the browser in a partially closed state that interferes with
/// proper functioning. See CloseBrowser() documentation for additional usage
/// information. This method must be called on the browser process UI thread.
///
/*--cef()--*/
virtual bool IsReadyToBeClosed() = 0;
///
/// Set whether the browser is focused.
///

View File

@@ -131,34 +131,44 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {}
///
/// Called when a browser has received a request to close. This may result
/// directly from a call to CefBrowserHost::*CloseBrowser() or indirectly if
/// the browser is parented to a top-level window created by CEF and the user
/// attempts to close that window (by clicking the 'X', for example). The
/// DoClose() method will be called after the JavaScript 'onunload' event has
/// been fired.
/// Called when an Alloy style browser is ready to be closed, meaning that the
/// close has already been initiated and that JavaScript unload handlers have
/// already executed or should be ignored. This may result directly from a
/// call to CefBrowserHost::[Try]CloseBrowser() or indirectly if the browser's
/// top-level parent window was created by CEF and the user attempts to
/// close that window (by clicking the 'X', for example). DoClose() will not
/// be called if the browser's host window/view has already been destroyed
/// (via parent window/view hierarchy tear-down, for example), as it is no
/// longer possible to customize the close behavior at that point.
///
/// An application should handle top-level owner window close notifications by
/// calling CefBrowserHost::TryCloseBrowser() or
/// An application should handle top-level parent window close notifications
/// by calling CefBrowserHost::TryCloseBrowser() or
/// CefBrowserHost::CloseBrowser(false) instead of allowing the window to
/// close immediately (see the examples below). This gives CEF an opportunity
/// to process the 'onbeforeunload' event and optionally cancel the close
/// to process JavaScript unload handlers and optionally cancel the close
/// before DoClose() is called.
///
/// When windowed rendering is enabled CEF will internally create a window or
/// view to host the browser. In that case returning false from DoClose() will
/// send the standard close notification to the browser's top-level owner
/// window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on
/// Linux or CefWindowDelegate::CanClose() callback from Views). If the
/// browser's host window/view has already been destroyed (via view hierarchy
/// tear-down, for example) then DoClose() will not be called for that browser
/// since is no longer possible to cancel the close.
/// When windowed rendering is enabled CEF will create an internal child
/// window/view to host the browser. In that case returning false from
/// DoClose() will send the standard close notification to the browser's
/// top-level parent window (e.g. WM_CLOSE on Windows, performClose: on OS X,
/// "delete_event" on Linux or CefWindowDelegate::CanClose() callback from
/// Views).
///
/// When windowed rendering is disabled returning false from DoClose() will
/// cause the browser object to be destroyed immediately.
/// When windowed rendering is disabled there is no internal window/view
/// and returning false from DoClose() will cause the browser object to be
/// destroyed immediately.
///
/// If the browser's top-level owner window requires a non-standard close
/// If the browser's top-level parent window requires a non-standard close
/// notification then send that notification from DoClose() and return true.
/// You are still required to complete the browser close as soon as possible
/// (either by calling [Try]CloseBrowser() or by proceeding with window/view
/// hierarchy tear-down), otherwise the browser will be left in a partially
/// closed state that interferes with proper functioning. Top-level windows
/// created on the browser process UI thread can alternately call
/// CefBrowserHost::IsReadyToBeClosed() in the close handler to check close
/// status instead of relying on custom DoClose() handling. See documentation
/// on that method for additional details.
///
/// The CefLifeSpanHandler::OnBeforeClose() method will be called after
/// DoClose() (if DoClose() is called) and immediately before the browser
@@ -174,7 +184,7 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
/// 1. User clicks the window close button which sends a close notification
/// to the application's top-level window.
/// 2. Application's top-level window receives the close notification and
/// calls TryCloseBrowser() (which internally calls CloseBrowser(false)).
/// calls TryCloseBrowser() (similar to calling CloseBrowser(false)).
/// TryCloseBrowser() returns false so the client cancels the window
/// close.
/// 3. JavaScript 'onbeforeunload' handler executes and shows the close
@@ -182,15 +192,18 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
/// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close.
/// 5. JavaScript 'onunload' handler executes.
/// 6. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false by default).
/// 7. Application's top-level window receives the close notification and
/// 6. Application's DoClose() handler is called and returns false by
/// default.
/// 7. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false).
/// 8. Application's top-level window receives the close notification and
/// calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
/// allows the window close.
/// 8. Application's top-level window is destroyed.
/// 9. Application's OnBeforeClose() handler is called and the browser object
/// 9. Application's top-level window is destroyed, triggering destruction
/// of the child browser window.
/// 10. Application's OnBeforeClose() handler is called and the browser object
/// is destroyed.
/// 10. Application exits by calling CefQuitMessageLoop() if no other browsers
/// 11. Application exits by calling CefQuitMessageLoop() if no other browsers
/// exist.
///
/// Example 2: Using CefBrowserHost::CloseBrowser(false) and implementing the
@@ -208,12 +221,15 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
/// 4. User approves the close.
/// 5. JavaScript 'onunload' handler executes.
/// 6. Application's DoClose() handler is called. Application will:
/// A. Set a flag to indicate that the next close attempt will be allowed.
/// A. Set a flag to indicate that the next top-level window close attempt
/// will be allowed.
/// B. Return false.
/// 7. CEF sends an close notification to the application's top-level window.
/// 7. CEF sends a close notification to the application's top-level window
/// (because DoClose() returned false).
/// 8. Application's top-level window receives the close notification and
/// allows the window to close based on the flag from #6B.
/// 9. Application's top-level window is destroyed.
/// allows the window to close based on the flag from #6A.
/// 9. Application's top-level window is destroyed, triggering destruction
/// of the child browser window.
/// 10. Application's OnBeforeClose() handler is called and the browser object
/// is destroyed.
/// 11. Application exits by calling CefQuitMessageLoop() if no other browsers