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 efe558cd28
commit 45861b1b08
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 // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=7c786570b1c7af912a31c6f0c3d742e8aeb38fd8$ // $hash=e9f34d90eb4af614e35cbb29da0639b62acec7fd$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
@ -301,29 +301,62 @@ typedef struct _cef_browser_host_t {
struct _cef_browser_host_t* self); struct _cef_browser_host_t* self);
/// ///
/// Request that the browser close. The JavaScript 'onbeforeunload' event will /// Request that the browser close. Closing a browser is a multi-stage process
/// be fired. If |force_close| is false (0) the event handler, if any, will be /// that may complete either synchronously or asynchronously, and involves
/// allowed to prompt the user and the user can optionally cancel the close. /// callbacks such as cef_life_span_handler_t::DoClose (Alloy style only),
/// If |force_close| is true (1) the prompt will not be displayed and the /// cef_life_span_handler_t::OnBeforeClose, and a top-level window close
/// close will proceed. Results in a call to /// handler such as cef_window_delegate_t::CanClose (or platform-specific
/// cef_life_span_handler_t::do_close() if the event handler allows the close /// equivalent). In some cases a close request may be delayed or canceled by
/// or if |force_close| is true (1). See cef_life_span_handler_t::do_close() /// the user. Using try_close_browser() instead of close_browser() is
/// documentation for additional usage information. /// 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, void(CEF_CALLBACK* close_browser)(struct _cef_browser_host_t* self,
int force_close); int force_close);
/// ///
/// Helper for closing a browser. Call this function from the top-level window /// Helper for closing a browser. This is similar in behavior to
/// close handler (if any). Internally this calls CloseBrowser(false (0)) if /// CLoseBrowser(false (0)) but returns a boolean to reflect the immediate
/// the close has not yet been initiated. This function returns false (0) /// close status. Call this function from a top-level window close handler
/// while the close is pending and true (1) after the close has completed. See /// such as cef_window_delegate_t::CanClose (or platform-specific equivalent)
/// close_browser() and cef_life_span_handler_t::do_close() documentation for /// to request that the browser close, and return the result to indicate if
/// additional usage information. This function must be called on the browser /// the window close should proceed. Returns false (0) if the close will be
/// process UI thread. /// 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); 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. /// 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 // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=5232dd6bf16af9b6d195a47bb41de0dfb880a65e$ // $hash=6aad2ccf30a6c519bbeee64d83866e82a41a48d8$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_ #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); struct _cef_browser_t* browser);
/// ///
/// Called when a browser has received a request to close. This may result /// Called when an Alloy style browser is ready to be closed, meaning that the
/// directly from a call to cef_browser_host_t::*close_browser() or indirectly /// close has already been initiated and that JavaScript unload handlers have
/// if the browser is parented to a top-level window created by CEF and the /// already executed or should be ignored. This may result directly from a
/// user attempts to close that window (by clicking the 'X', for example). The /// call to cef_browser_host_t::[Try]close_browser() or indirectly if the
/// do_close() function will be called after the JavaScript 'onunload' event /// browser's top-level parent window was created by CEF and the user attempts
/// has been fired. /// 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 /// An application should handle top-level parent window close notifications
/// calling cef_browser_host_t::try_close_browser() or /// by calling cef_browser_host_t::try_close_browser() or
/// cef_browser_host_t::CloseBrowser(false (0)) instead of allowing the window /// cef_browser_host_t::CloseBrowser(false (0)) instead of allowing the window
/// to close immediately (see the examples below). This gives CEF an /// 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. /// the close before do_close() is called.
/// ///
/// When windowed rendering is enabled CEF will internally create a window or /// When windowed rendering is enabled CEF will create an internal child
/// view to host the browser. In that case returning false (0) from do_close() /// window/view to host the browser. In that case returning false (0) from
/// will send the standard close notification to the browser's top-level owner /// do_close() will send the standard close notification to the browser's top-
/// window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on /// level parent window (e.g. WM_CLOSE on Windows, performClose: on OS X,
/// Linux or cef_window_delegate_t::can_close() callback from Views). If the /// "delete_event" on Linux or cef_window_delegate_t::can_close() callback
/// browser's host window/view has already been destroyed (via view hierarchy /// from Views).
/// 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 disabled returning false (0) from do_close() /// When windowed rendering is disabled there is no internal window/view and
/// will cause the browser object to be destroyed immediately. /// 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 /// 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 /// 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 /// 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 /// which sends a close notification
/// to the application's top-level window. /// to the application's top-level window.
/// 2. Application's top-level window receives the close notification and /// 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 /// TryCloseBrowser() returns false so the client cancels the window
/// close. /// close.
/// 3. JavaScript 'onbeforeunload' handler executes and shows the close /// 3. JavaScript 'onbeforeunload' handler executes and shows the close
/// confirmation dialog (which can be overridden via /// confirmation dialog (which can be overridden via
/// CefJSDialogHandler::OnBeforeUnloadDialog()). /// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close. 5. JavaScript 'onunload' handler executes. /// 4. User approves the close. 5. JavaScript 'onunload' handler executes.
/// 6. CEF sends a close notification to the application's top-level window /// 6. Application's do_close() handler is called and returns false (0) by
/// (because DoClose() returned false by default). /// default.
/// 7. Application's top-level window receives the close notification and /// 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 /// calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
/// allows the window close. /// allows the window close.
/// 8. Application's top-level window is destroyed. 9. Application's /// 9. Application's top-level window is destroyed, triggering destruction
/// on_before_close() handler is called and the browser object /// of the child browser window.
/// 10. Application's on_before_close() handler is called and the browser
/// object
/// is destroyed. /// 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 /// browsers
/// exist. /// exist.
/// ///
@ -215,13 +228,17 @@ typedef struct _cef_life_span_handler_t {
/// CefJSDialogHandler::OnBeforeUnloadDialog()). /// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close. 5. JavaScript 'onunload' handler executes. /// 4. User approves the close. 5. JavaScript 'onunload' handler executes.
/// 6. Application's do_close() handler is called. Application will: /// 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. /// 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 /// 8. Application's top-level window receives the close notification and
/// allows the window to close based on the flag from #6B. /// allows the window to close based on the flag from #6A.
/// 9. Application's top-level window is destroyed. 10. Application's /// 9. Application's top-level window is destroyed, triggering destruction
/// on_before_close() handler is called and the browser object /// of the child browser window.
/// 10. Application's on_before_close() handler is called and the browser
/// object
/// is destroyed. /// is destroyed.
/// 11. Application exits by calling cef_quit_message_loop() if no other /// 11. Application exits by calling cef_quit_message_loop() if no other
/// browsers /// browsers

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal // way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash // hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected. // 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) #if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "072a4fe61a512f21fd0d664495902fca6ec2193b" #define CEF_API_HASH_PLATFORM "51848171cdea10858c4e0fca0f7099b0fdc759f9"
#elif defined(OS_MAC) #elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "ee7f0e9247add8df0827d02da32559e38e8079d0" #define CEF_API_HASH_PLATFORM "8cc826c5f5fe97c275dfa3b9c020470678a5d2fd"
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "88996e58ee062016efd054bfbafd03dd3daa99ed" #define CEF_API_HASH_PLATFORM "0aec2348de1bf14fafa7a23baa0df942d342d409"
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -331,30 +331,63 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
virtual CefRefPtr<CefBrowser> GetBrowser() = 0; virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
/// ///
/// Request that the browser close. The JavaScript 'onbeforeunload' event will /// Request that the browser close. Closing a browser is a multi-stage process
/// be fired. If |force_close| is false the event handler, if any, will be /// that may complete either synchronously or asynchronously, and involves
/// allowed to prompt the user and the user can optionally cancel the close. /// callbacks such as CefLifeSpanHandler::DoClose (Alloy style only),
/// If |force_close| is true the prompt will not be displayed and the close /// CefLifeSpanHandler::OnBeforeClose, and a top-level window close handler
/// will proceed. Results in a call to CefLifeSpanHandler::DoClose() if the /// such as CefWindowDelegate::CanClose (or platform-specific equivalent). In
/// event handler allows the close or if |force_close| is true. See /// some cases a close request may be delayed or canceled by the user. Using
/// CefLifeSpanHandler::DoClose() documentation for additional usage /// TryCloseBrowser() instead of CloseBrowser() is recommended for most use
/// information. /// 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()--*/ /*--cef()--*/
virtual void CloseBrowser(bool force_close) = 0; virtual void CloseBrowser(bool force_close) = 0;
/// ///
/// Helper for closing a browser. Call this method from the top-level window /// Helper for closing a browser. This is similar in behavior to
/// close handler (if any). Internally this calls CloseBrowser(false) if the /// CLoseBrowser(false) but returns a boolean to reflect the immediate close
/// close has not yet been initiated. This method returns false while the /// status. Call this method from a top-level window close handler such as
/// close is pending and true after the close has completed. See /// CefWindowDelegate::CanClose (or platform-specific equivalent) to request
/// CloseBrowser() and CefLifeSpanHandler::DoClose() documentation for /// that the browser close, and return the result to indicate if the window
/// additional usage information. This method must be called on the browser /// close should proceed. Returns false if the close will be delayed
/// process UI thread. /// (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()--*/ /*--cef()--*/
virtual bool TryCloseBrowser() = 0; 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. /// Set whether the browser is focused.
/// ///

View File

@ -131,34 +131,44 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {} virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {}
/// ///
/// Called when a browser has received a request to close. This may result /// Called when an Alloy style browser is ready to be closed, meaning that the
/// directly from a call to CefBrowserHost::*CloseBrowser() or indirectly if /// close has already been initiated and that JavaScript unload handlers have
/// the browser is parented to a top-level window created by CEF and the user /// already executed or should be ignored. This may result directly from a
/// attempts to close that window (by clicking the 'X', for example). The /// call to CefBrowserHost::[Try]CloseBrowser() or indirectly if the browser's
/// DoClose() method will be called after the JavaScript 'onunload' event has /// top-level parent window was created by CEF and the user attempts to
/// been fired. /// 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 /// An application should handle top-level parent window close notifications
/// calling CefBrowserHost::TryCloseBrowser() or /// by calling CefBrowserHost::TryCloseBrowser() or
/// CefBrowserHost::CloseBrowser(false) instead of allowing the window to /// CefBrowserHost::CloseBrowser(false) instead of allowing the window to
/// close immediately (see the examples below). This gives CEF an opportunity /// 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. /// before DoClose() is called.
/// ///
/// When windowed rendering is enabled CEF will internally create a window or /// When windowed rendering is enabled CEF will create an internal child
/// view to host the browser. In that case returning false from DoClose() will /// window/view to host the browser. In that case returning false from
/// send the standard close notification to the browser's top-level owner /// DoClose() will send the standard close notification to the browser's
/// window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on /// top-level parent window (e.g. WM_CLOSE on Windows, performClose: on OS X,
/// Linux or CefWindowDelegate::CanClose() callback from Views). If the /// "delete_event" on Linux or CefWindowDelegate::CanClose() callback from
/// browser's host window/view has already been destroyed (via view hierarchy /// Views).
/// 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 disabled returning false from DoClose() will /// When windowed rendering is disabled there is no internal window/view
/// cause the browser object to be destroyed immediately. /// 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. /// 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 /// The CefLifeSpanHandler::OnBeforeClose() method will be called after
/// DoClose() (if DoClose() is called) and immediately before the browser /// 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 /// 1. User clicks the window close button which sends a close notification
/// to the application's top-level window. /// to the application's top-level window.
/// 2. Application's top-level window receives the close notification and /// 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 /// TryCloseBrowser() returns false so the client cancels the window
/// close. /// close.
/// 3. JavaScript 'onbeforeunload' handler executes and shows the close /// 3. JavaScript 'onbeforeunload' handler executes and shows the close
@ -182,15 +192,18 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
/// CefJSDialogHandler::OnBeforeUnloadDialog()). /// CefJSDialogHandler::OnBeforeUnloadDialog()).
/// 4. User approves the close. /// 4. User approves the close.
/// 5. JavaScript 'onunload' handler executes. /// 5. JavaScript 'onunload' handler executes.
/// 6. CEF sends a close notification to the application's top-level window /// 6. Application's DoClose() handler is called and returns false by
/// (because DoClose() returned false by default). /// default.
/// 7. Application's top-level window receives the close notification and /// 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 /// calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
/// allows the window close. /// allows the window close.
/// 8. Application's top-level window is destroyed. /// 9. Application's top-level window is destroyed, triggering destruction
/// 9. Application's OnBeforeClose() handler is called and the browser object /// of the child browser window.
/// 10. Application's OnBeforeClose() handler is called and the browser object
/// is destroyed. /// is destroyed.
/// 10. Application exits by calling CefQuitMessageLoop() if no other browsers /// 11. Application exits by calling CefQuitMessageLoop() if no other browsers
/// exist. /// exist.
/// ///
/// Example 2: Using CefBrowserHost::CloseBrowser(false) and implementing the /// Example 2: Using CefBrowserHost::CloseBrowser(false) and implementing the
@ -208,12 +221,15 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted {
/// 4. User approves the close. /// 4. User approves the close.
/// 5. JavaScript 'onunload' handler executes. /// 5. JavaScript 'onunload' handler executes.
/// 6. Application's DoClose() handler is called. Application will: /// 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. /// 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 /// 8. Application's top-level window receives the close notification and
/// allows the window to close based on the flag from #6B. /// allows the window to close based on the flag from #6A.
/// 9. Application's top-level window is destroyed. /// 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 /// 10. Application's OnBeforeClose() handler is called and the browser object
/// is destroyed. /// is destroyed.
/// 11. Application exits by calling CefQuitMessageLoop() if no other browsers /// 11. Application exits by calling CefQuitMessageLoop() if no other browsers

View File

@ -452,6 +452,20 @@ bool CefBrowserHostBase::HasView() {
return is_views_hosted_; return is_views_hosted_;
} }
bool CefBrowserHostBase::IsReadyToBeClosed() {
if (!CEF_CURRENTLY_ON_UIT()) {
DCHECK(false) << "called on invalid thread";
return false;
}
if (auto web_contents = GetWebContents()) {
return static_cast<content::RenderFrameHostImpl*>(
web_contents->GetPrimaryMainFrame())
->IsPageReadyToBeClosed();
}
return true;
}
void CefBrowserHostBase::SetFocus(bool focus) { void CefBrowserHostBase::SetFocus(bool focus) {
if (!CEF_CURRENTLY_ON_UIT()) { if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, CEF_POST_TASK(CEF_UIT,

View File

@ -218,6 +218,7 @@ class CefBrowserHostBase : public CefBrowserHost,
double GetZoomLevel() override; double GetZoomLevel() override;
void SetZoomLevel(double zoomLevel) override; void SetZoomLevel(double zoomLevel) override;
bool HasView() override; bool HasView() override;
bool IsReadyToBeClosed() override;
void SetFocus(bool focus) override; void SetFocus(bool focus) override;
void RunFileDialog(FileDialogMode mode, void RunFileDialog(FileDialogMode mode,
const CefString& title, const CefString& title,

View File

@ -165,14 +165,47 @@ void ChromeBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
} }
void ChromeBrowserHostImpl::CloseBrowser(bool force_close) { void ChromeBrowserHostImpl::CloseBrowser(bool force_close) {
// Always do this asynchronously because TabStripModel is not re-entrant. if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::DoCloseBrowser, CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::CloseBrowser,
this, force_close)); this, force_close));
return;
}
if (!force_close) {
TryCloseBrowser();
return;
}
// Always do this asynchronously because TabStripModel is not re-entrant.
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&ChromeBrowserHostImpl::DoCloseBrowser, this));
} }
bool ChromeBrowserHostImpl::TryCloseBrowser() { bool ChromeBrowserHostImpl::TryCloseBrowser() {
// TODO(chrome): Handle the case where the browser may not close immediately. if (!CEF_CURRENTLY_ON_UIT()) {
CloseBrowser(true); DCHECK(false) << "called on invalid thread";
return false;
}
if (auto* web_contents = GetWebContents()) {
// This check works as follows:
// 1. Returns false if the main frame is ready to close
// (IsPageReadyToBeClosed returns true).
// 2. Otherwise returns true if any frame in the frame tree needs to run
// beforeunload or unload-time event handlers.
// 3. Otherwise returns false.
if (web_contents->NeedToFireBeforeUnloadOrUnloadEvents()) {
// Will result in a call to Browser::BeforeUnloadFired and, if the close
// isn't canceled, Browser::CloseContents which indirectly calls
// TabStripModel::CloseWebContentsAt (similar to DoCloseBrowser but
// without CLOSE_USER_GESTURE). Additional calls to DispatchBeforeUnload
// while the unload is pending will be ignored.
web_contents->DispatchBeforeUnload(/*auto_cancel=*/false);
return false;
}
}
CloseBrowser(/*force_close=*/true);
return true; return true;
} }
@ -551,7 +584,7 @@ void ChromeBrowserHostImpl::DestroyBrowser() {
// WebContents first. See comments on CefBrowserHostBase::DestroyBrowser. // WebContents first. See comments on CefBrowserHostBase::DestroyBrowser.
if (GetWebContents()) { if (GetWebContents()) {
// Triggers a call to OnWebContentsDestroyed. // Triggers a call to OnWebContentsDestroyed.
DoCloseBrowser(/*force_close=*/true); DoCloseBrowser();
DCHECK(!GetWebContents()); DCHECK(!GetWebContents());
} }
@ -565,15 +598,13 @@ void ChromeBrowserHostImpl::DestroyBrowser() {
CefBrowserHostBase::DestroyBrowser(); CefBrowserHostBase::DestroyBrowser();
} }
void ChromeBrowserHostImpl::DoCloseBrowser(bool force_close) { void ChromeBrowserHostImpl::DoCloseBrowser() {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
if (browser_) { if (browser_) {
// Like chrome::CloseTab() but specifying the WebContents. // Like chrome::CloseTab() but specifying the WebContents.
const int tab_index = GetCurrentTabIndex(); const int tab_index = GetCurrentTabIndex();
if (tab_index != TabStripModel::kNoTab) { if (tab_index != TabStripModel::kNoTab) {
// This will trigger destruction of the Browser and WebContents. // This will trigger destruction of the Browser and WebContents.
// TODO(chrome): Handle the case where this method returns false,
// indicating that the contents were not closed immediately.
browser_->tab_strip_model()->CloseWebContentsAt( browser_->tab_strip_model()->CloseWebContentsAt(
tab_index, TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB | tab_index, TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB |
TabCloseTypes::CLOSE_USER_GESTURE); TabCloseTypes::CLOSE_USER_GESTURE);

View File

@ -169,7 +169,7 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase {
bool WillBeDestroyed() const override; bool WillBeDestroyed() const override;
void DestroyBrowser() override; void DestroyBrowser() override;
void DoCloseBrowser(bool force_close); void DoCloseBrowser();
// Returns the current tab index for the associated WebContents, or // Returns the current tab index for the associated WebContents, or
// TabStripModel::kNoTab if not found. // TabStripModel::kNoTab if not found.

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=434a753c90262b051077f7a79f3106ac52ffbf75$ // $hash=8a2a8a4853c3869876ffad3e6c175945ac1c5021$
// //
#include "libcef_dll/cpptoc/browser_host_cpptoc.h" #include "libcef_dll/cpptoc/browser_host_cpptoc.h"
@ -191,6 +191,24 @@ browser_host_try_close_browser(struct _cef_browser_host_t* self) {
return _retval; return _retval;
} }
int CEF_CALLBACK
browser_host_is_ready_to_be_closed(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)->IsReadyToBeClosed();
// Return type: bool
return _retval;
}
void CEF_CALLBACK browser_host_set_focus(struct _cef_browser_host_t* self, void CEF_CALLBACK browser_host_set_focus(struct _cef_browser_host_t* self,
int focus) { int focus) {
shutdown_checker::AssertNotShutdown(); shutdown_checker::AssertNotShutdown();
@ -1514,6 +1532,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
GetStruct()->get_browser = browser_host_get_browser; GetStruct()->get_browser = browser_host_get_browser;
GetStruct()->close_browser = browser_host_close_browser; GetStruct()->close_browser = browser_host_close_browser;
GetStruct()->try_close_browser = browser_host_try_close_browser; GetStruct()->try_close_browser = browser_host_try_close_browser;
GetStruct()->is_ready_to_be_closed = browser_host_is_ready_to_be_closed;
GetStruct()->set_focus = browser_host_set_focus; GetStruct()->set_focus = browser_host_set_focus;
GetStruct()->get_window_handle = browser_host_get_window_handle; GetStruct()->get_window_handle = browser_host_get_window_handle;
GetStruct()->get_opener_window_handle = browser_host_get_opener_window_handle; GetStruct()->get_opener_window_handle = browser_host_get_opener_window_handle;

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=dac19ba091b3acf3e1587b176e28bc9f9c8c8dd0$ // $hash=2319d794dd3a38c448908114d1b4ea37b34f89dd$
// //
#include "libcef_dll/ctocpp/browser_host_ctocpp.h" #include "libcef_dll/ctocpp/browser_host_ctocpp.h"
@ -131,6 +131,23 @@ NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::TryCloseBrowser() {
return _retval ? true : false; return _retval ? true : false;
} }
NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::IsReadyToBeClosed() {
shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, is_ready_to_be_closed)) {
return false;
}
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
int _retval = _struct->is_ready_to_be_closed(_struct);
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SetFocus(bool focus) { NO_SANITIZE("cfi-icall") void CefBrowserHostCToCpp::SetFocus(bool focus) {
shutdown_checker::AssertNotShutdown(); shutdown_checker::AssertNotShutdown();
@ -1111,8 +1128,8 @@ void CefBrowserHostCToCpp::DragSourceSystemDragEnded() {
} }
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
CefRefPtr< CefRefPtr<CefNavigationEntry>
CefNavigationEntry> CefBrowserHostCToCpp::GetVisibleNavigationEntry() { CefBrowserHostCToCpp::GetVisibleNavigationEntry() {
shutdown_checker::AssertNotShutdown(); shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct(); cef_browser_host_t* _struct = GetStruct();

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=9f40e4ce3e46a895b5bf644bebdc2d802c9b598b$ // $hash=73d8659f17a4ae3319b5bf20807d5c69a1759c04$
// //
#ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_ #ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
@ -41,6 +41,7 @@ class CefBrowserHostCToCpp : public CefCToCppRefCounted<CefBrowserHostCToCpp,
CefRefPtr<CefBrowser> GetBrowser() override; CefRefPtr<CefBrowser> GetBrowser() override;
void CloseBrowser(bool force_close) override; void CloseBrowser(bool force_close) override;
bool TryCloseBrowser() override; bool TryCloseBrowser() override;
bool IsReadyToBeClosed() override;
void SetFocus(bool focus) override; void SetFocus(bool focus) override;
CefWindowHandle GetWindowHandle() override; CefWindowHandle GetWindowHandle() override;
CefWindowHandle GetOpenerWindowHandle() override; CefWindowHandle GetOpenerWindowHandle() override;

View File

@ -6,6 +6,7 @@
#include "include/test/cef_test_helpers.h" #include "include/test/cef_test_helpers.h"
#include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/routing_test_handler.h" #include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h" #include "tests/gtest/include/gtest/gtest.h"
namespace { namespace {
@ -58,6 +59,8 @@ class LifeSpanTestHandler : public RoutingTestHandler {
} }
bool DoClose(CefRefPtr<CefBrowser> browser) override { bool DoClose(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser->GetHost()->IsReadyToBeClosed());
if (executing_delay_close_) { if (executing_delay_close_) {
return false; return false;
} }
@ -75,6 +78,8 @@ class LifeSpanTestHandler : public RoutingTestHandler {
} }
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser->GetHost()->IsReadyToBeClosed());
if (!executing_delay_close_) { if (!executing_delay_close_) {
got_before_close_.yes(); got_before_close_.yes();
EXPECT_TRUE(browser->IsSame(GetBrowser())); EXPECT_TRUE(browser->IsSame(GetBrowser()));
@ -87,6 +92,8 @@ class LifeSpanTestHandler : public RoutingTestHandler {
const CefString& message_text, const CefString& message_text,
bool is_reload, bool is_reload,
CefRefPtr<CefJSDialogCallback> callback) override { CefRefPtr<CefJSDialogCallback> callback) override {
EXPECT_FALSE(browser->GetHost()->IsReadyToBeClosed());
if (executing_delay_close_) { if (executing_delay_close_) {
callback->Continue(true, CefString()); callback->Continue(true, CefString());
return true; return true;
@ -124,6 +131,8 @@ class LifeSpanTestHandler : public RoutingTestHandler {
CefExecuteJavaScriptWithUserGestureForTests(frame, CefString()); CefExecuteJavaScriptWithUserGestureForTests(frame, CefString());
} }
EXPECT_FALSE(browser->GetHost()->IsReadyToBeClosed());
// Attempt to close the browser. // Attempt to close the browser.
CloseBrowser(browser, settings_.force_close); CloseBrowser(browser, settings_.force_close);
} }
@ -180,12 +189,15 @@ class LifeSpanTestHandler : public RoutingTestHandler {
TEST(LifeSpanTest, DoCloseAllow) { TEST(LifeSpanTest, DoCloseAllow) {
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = true;
CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings); CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
handler->ExecuteTest(); handler->ExecuteTest();
EXPECT_TRUE(handler->got_after_created_); EXPECT_TRUE(handler->got_after_created_);
if (handler->use_alloy_style_browser()) {
EXPECT_TRUE(handler->got_do_close_); EXPECT_TRUE(handler->got_do_close_);
} else {
EXPECT_FALSE(handler->got_do_close_);
}
EXPECT_TRUE(handler->got_before_close_); EXPECT_TRUE(handler->got_before_close_);
EXPECT_FALSE(handler->got_before_unload_dialog_); EXPECT_FALSE(handler->got_before_unload_dialog_);
EXPECT_TRUE(handler->got_unload_message_); EXPECT_TRUE(handler->got_unload_message_);
@ -197,13 +209,16 @@ TEST(LifeSpanTest, DoCloseAllow) {
TEST(LifeSpanTest, DoCloseAllowForce) { TEST(LifeSpanTest, DoCloseAllowForce) {
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = true;
settings.force_close = true; settings.force_close = true;
CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings); CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
handler->ExecuteTest(); handler->ExecuteTest();
EXPECT_TRUE(handler->got_after_created_); EXPECT_TRUE(handler->got_after_created_);
if (handler->use_alloy_style_browser()) {
EXPECT_TRUE(handler->got_do_close_); EXPECT_TRUE(handler->got_do_close_);
} else {
EXPECT_FALSE(handler->got_do_close_);
}
EXPECT_TRUE(handler->got_before_close_); EXPECT_TRUE(handler->got_before_close_);
EXPECT_FALSE(handler->got_before_unload_dialog_); EXPECT_FALSE(handler->got_before_unload_dialog_);
EXPECT_TRUE(handler->got_unload_message_); EXPECT_TRUE(handler->got_unload_message_);
@ -214,6 +229,11 @@ TEST(LifeSpanTest, DoCloseAllowForce) {
} }
TEST(LifeSpanTest, DoCloseDisallow) { TEST(LifeSpanTest, DoCloseDisallow) {
// Test not supported with Chrome style browser.
if (!UseAlloyStyleBrowserGlobal()) {
return;
}
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = false; settings.allow_do_close = false;
CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings); CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
@ -231,6 +251,11 @@ TEST(LifeSpanTest, DoCloseDisallow) {
} }
TEST(LifeSpanTest, DoCloseDisallowForce) { TEST(LifeSpanTest, DoCloseDisallowForce) {
// Test not supported with Chrome style browser.
if (!UseAlloyStyleBrowserGlobal()) {
return;
}
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = false; settings.allow_do_close = false;
settings.force_close = true; settings.force_close = true;
@ -249,6 +274,11 @@ TEST(LifeSpanTest, DoCloseDisallowForce) {
} }
TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadAllow) { TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadAllow) {
// Test not supported with Chrome style browser.
if (!UseAlloyStyleBrowserGlobal()) {
return;
}
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = false; settings.allow_do_close = false;
settings.add_onunload_handler = true; settings.add_onunload_handler = true;
@ -269,14 +299,17 @@ TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadAllow) {
TEST(LifeSpanTest, DoCloseAllowWithOnUnloadForce) { TEST(LifeSpanTest, DoCloseAllowWithOnUnloadForce) {
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = true;
settings.add_onunload_handler = true; settings.add_onunload_handler = true;
settings.force_close = true; settings.force_close = true;
CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings); CefRefPtr<LifeSpanTestHandler> handler = new LifeSpanTestHandler(settings);
handler->ExecuteTest(); handler->ExecuteTest();
EXPECT_TRUE(handler->got_after_created_); EXPECT_TRUE(handler->got_after_created_);
if (handler->use_alloy_style_browser()) {
EXPECT_TRUE(handler->got_do_close_); EXPECT_TRUE(handler->got_do_close_);
} else {
EXPECT_FALSE(handler->got_do_close_);
}
EXPECT_TRUE(handler->got_before_close_); EXPECT_TRUE(handler->got_before_close_);
EXPECT_TRUE(handler->got_before_unload_dialog_); EXPECT_TRUE(handler->got_before_unload_dialog_);
EXPECT_TRUE(handler->got_unload_message_); EXPECT_TRUE(handler->got_unload_message_);
@ -287,6 +320,11 @@ TEST(LifeSpanTest, DoCloseAllowWithOnUnloadForce) {
} }
TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadForce) { TEST(LifeSpanTest, DoCloseDisallowWithOnUnloadForce) {
// Test not supported with Chrome style browser.
if (!UseAlloyStyleBrowserGlobal()) {
return;
}
LifeSpanTestHandler::Settings settings; LifeSpanTestHandler::Settings settings;
settings.allow_do_close = false; settings.allow_do_close = false;
settings.add_onunload_handler = true; settings.add_onunload_handler = true;
@ -313,7 +351,11 @@ TEST(LifeSpanTest, OnUnloadAllow) {
handler->ExecuteTest(); handler->ExecuteTest();
EXPECT_TRUE(handler->got_after_created_); EXPECT_TRUE(handler->got_after_created_);
if (handler->use_alloy_style_browser()) {
EXPECT_TRUE(handler->got_do_close_); EXPECT_TRUE(handler->got_do_close_);
} else {
EXPECT_FALSE(handler->got_do_close_);
}
EXPECT_TRUE(handler->got_before_close_); EXPECT_TRUE(handler->got_before_close_);
EXPECT_TRUE(handler->got_before_unload_dialog_); EXPECT_TRUE(handler->got_before_unload_dialog_);
EXPECT_TRUE(handler->got_unload_message_); EXPECT_TRUE(handler->got_unload_message_);

View File

@ -322,6 +322,8 @@ void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
EXPECT_UI_THREAD(); EXPECT_UI_THREAD();
EXPECT_TRUE(browser->GetHost()->IsReadyToBeClosed());
// Free the browser pointer so that the browser can be destroyed. // Free the browser pointer so that the browser can be destroyed.
const int browser_id = browser->GetIdentifier(); const int browser_id = browser->GetIdentifier();
BrowserMap::iterator it = browser_map_.find(browser_id); BrowserMap::iterator it = browser_map_.find(browser_id);