Compare commits

...

14 Commits

Author SHA1 Message Date
Marshall Greenblatt
48f3ef63e2 linux: Fix callbacks to CefPrintHandler (fixes #3729)
Out-of-process printing must also be disabled by passing
`--disable-features=EnableOopPrintDrivers`.
2024-10-18 15:04:17 -04:00
Marshall Greenblatt
7268dc8cd3 cefclient: views: Add ability to pop out the overlay Browser (see #3790)
When running with the overlay Browser enabled (`--show-overlay-browser`),
pressing Alt+O will move the overlay Browser to a new top-level Window.
Pressing Alt+O again or closing the new Window via the close button will
return the Browser to the overlay. Closing the Browser via `window.close()`
(in the new Window or overlay) will dismiss the overlay completely as
required to maintain consistent internal state. Detection of this state is
supported by the new CefBrowserHost::IsReadyToBeClosed method.

Draggable regions in the main Browser are updated to account for the
presence or absence of the overlay Browser. Support for draggable regions
in the overlay Browser in not implemented in cefclient.

Behavior with multiple overlays, `window.close()` and draggable regions
can be tested by adding `--hide-frame --hide-controls`.
2024-10-18 15:04:08 -04:00
Marshall Greenblatt
45861b1b08 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.
2024-10-18 15:03:59 -04:00
Marshall Greenblatt
efe558cd28 views: Fix dangling raw_ptr on CefToolbarViewView destruction 2024-10-18 15:03:49 -04:00
Marshall Greenblatt
f1e634393f views: cefclient: Fix crash when ID_MENU_BUTTON doesn't exist 2024-10-18 15:03:40 -04:00
Marshall Greenblatt
0187046a2e views: Trigger CefBrowser destruction on CefBrowserView release (see #3790)
Avoid a circular ownership dependency between CefBrowserViewImpl and
CefBrowserPlatformDelegate[Chrome]Views (owned by CefBrowserHostBase).
Trigger CefBrowserHostBase destruction when the last CefBrowserViewImpl
reference is released.

This fixes a number of shutdown crashes related to overlay CefBrowsers
still existing at CefShutdown.
2024-10-18 15:03:29 -04:00
Marshall Greenblatt
6f4c2bf8df Fix DiscardableSharedMemoryManager destruction timing
Move DiscardableSharedMemoryManager destruction to the end of UI thread
shutdown, after Mojo messages have stopped arriving. See related comments
at https://chromium-review.googlesource.com/c/chromium/src/+/5925441
2024-10-18 15:03:19 -04:00
Marshall Greenblatt
de2da368c6 cefclient: views: Add overlay BrowserView demo (see #3790)
Adds new `--show-overlay-browser` command-line flag that creates an
overlay containing an Alloy-style BrowserView.  The main BrowserView
may be Chrome- (default) or Alloy-style (add `--use-alloy-style`).
This overlay will only be added to standalone (non-popup) windows.

Popups triggered via the overlay will receive standard Alloy-style
handling in cefclient (e.g. Views-hosted window). Add
`--use-default-popup` for default popup handling.
2024-10-18 15:03:08 -04:00
Marshall Greenblatt
4797681694 views: Fix style calculation for popup BrowserViews (fixes #3499)
Don't depend on a CefBrowserViewDelegate for popup BrowserView style.
Popup style must always match the opener style.
2024-10-18 15:02:57 -04:00
Nik Pavlov
ffbc53a9e6 views: Fix overlay bounds outside of window (fixes #3457) 2024-10-18 15:02:46 -04:00
Marshall Greenblatt
92f14410ae wrapper: Add missing algorithm include (fixes #3804) 2024-10-18 15:02:35 -04:00
Nik Pavlov
e68b0169a1 ceftests: Simplify ViewsWindowTest.WindowAccelerator test (see #3188)
Remove the CefWindowDelegate::OnKeyEvent check as that method
is not currently triggered with the Chrome runtime (see #3797).
2024-10-18 15:02:24 -04:00
Nik Pavlov
1fd6000c70 mac: views: Always initialize CommandDispatcher (see #3188)
ChromeCommandDispatcherDelegate forwards accelerator events to
the FocusManager through NativeWidgetMacNSWindowHost::
HandleAccelerator. Always initialize it so that accelerators also
work in a CefWindow without a BrowserView.
2024-10-18 15:02:14 -04:00
Marshall Greenblatt
5e348cb1fc cefclient: Fix toolbar visibility with --hide-controls (fixes #3745) 2024-10-18 15:02:01 -04:00
42 changed files with 1075 additions and 253 deletions

View File

@ -288,6 +288,8 @@
'tests/cefclient/browser/urlrequest_test.h', 'tests/cefclient/browser/urlrequest_test.h',
'tests/cefclient/browser/views_menu_bar.cc', 'tests/cefclient/browser/views_menu_bar.cc',
'tests/cefclient/browser/views_menu_bar.h', 'tests/cefclient/browser/views_menu_bar.h',
'tests/cefclient/browser/views_overlay_browser.cc',
'tests/cefclient/browser/views_overlay_browser.h',
'tests/cefclient/browser/views_overlay_controls.cc', 'tests/cefclient/browser/views_overlay_controls.cc',
'tests/cefclient/browser/views_overlay_controls.h', 'tests/cefclient/browser/views_overlay_controls.h',
'tests/cefclient/browser/views_style.cc', 'tests/cefclient/browser/views_style.cc',

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

@ -63,6 +63,10 @@ class PopupWindowDelegate : public CefWindowDelegate {
return true; return true;
} }
cef_runtime_style_t GetWindowRuntimeStyle() override {
return browser_view_->GetRuntimeStyle();
}
private: private:
CefRefPtr<CefBrowserView> browser_view_; CefRefPtr<CefBrowserView> browser_view_;
@ -195,8 +199,9 @@ void CefBrowserPlatformDelegate::PopupWebContentsCreated(
} }
CefRefPtr<CefBrowserViewDelegate> new_delegate; CefRefPtr<CefBrowserViewDelegate> new_delegate;
CefRefPtr<CefBrowserViewDelegate> opener_delegate; CefRefPtr<CefBrowserViewDelegate> opener_delegate;
cef_runtime_style_t opener_runtime_style = CEF_RUNTIME_STYLE_DEFAULT;
auto browser_view = GetBrowserView(); auto browser_view = GetBrowserView();
if (browser_view) { if (browser_view) {
// When |this| (the popup opener) is Views-hosted use the current delegate. // When |this| (the popup opener) is Views-hosted use the current delegate.
@ -212,12 +217,22 @@ void CefBrowserPlatformDelegate::PopupWebContentsCreated(
browser_view, settings, client, is_devtools); browser_view, settings, client, is_devtools);
} }
if (browser_view) {
opener_runtime_style = browser_view->GetRuntimeStyle();
} else if (opener_delegate) {
opener_runtime_style = opener_delegate->GetBrowserRuntimeStyle();
}
// Create a new BrowserView for the popup. // Create a new BrowserView for the popup.
CefRefPtr<CefBrowserViewImpl> new_browser_view = CefRefPtr<CefBrowserViewImpl> new_browser_view =
CefBrowserViewImpl::CreateForPopup(settings, new_delegate, is_devtools); CefBrowserViewImpl::CreateForPopup(settings, new_delegate, is_devtools,
opener_runtime_style);
// Associate the PlatformDelegate with the new BrowserView. // Associate the PlatformDelegate with the new BrowserView.
new_platform_delegate->SetBrowserView(new_browser_view); new_platform_delegate->SetBrowserView(new_browser_view);
// Keep the BrowserView alive until PopupBrowserCreated() is called.
new_browser_view->AddRef();
} }
void CefBrowserPlatformDelegate::PopupBrowserCreated( void CefBrowserPlatformDelegate::PopupBrowserCreated(
@ -255,6 +270,9 @@ void CefBrowserPlatformDelegate::PopupBrowserCreated(
CefWindow::CreateTopLevelWindow( CefWindow::CreateTopLevelWindow(
new PopupWindowDelegate(new_browser_view.get())); new PopupWindowDelegate(new_browser_view.get()));
} }
// Release the reference added in PopupWebContentsCreated().
new_browser_view->Release();
} }
CefRefPtr<CefBrowserViewDelegate> CefRefPtr<CefBrowserViewDelegate>

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

@ -17,6 +17,7 @@
#if BUILDFLAG(IS_LINUX) #if BUILDFLAG(IS_LINUX)
#include "base/linux_util.h" #include "base/linux_util.h"
#include "cef/libcef/browser/printing/print_dialog_linux.h"
#endif #endif
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
@ -83,4 +84,11 @@ void ChromeBrowserMainExtraPartsCef::ToolkitInitialized() {
// Override the default Chrome client. // Override the default Chrome client.
SetConstrainedWindowViewsClient(CreateAlloyConstrainedWindowViewsClient( SetConstrainedWindowViewsClient(CreateAlloyConstrainedWindowViewsClient(
CreateChromeConstrainedWindowViewsClient())); CreateChromeConstrainedWindowViewsClient()));
#if BUILDFLAG(IS_LINUX)
auto printing_delegate = new CefPrintingContextLinuxDelegate();
auto default_delegate =
ui::PrintingContextLinuxDelegate::SetInstance(printing_delegate);
printing_delegate->SetDefaultDelegate(default_delegate);
#endif // BUILDFLAG(IS_LINUX)
} }

View File

@ -24,7 +24,8 @@ void CefBrowserPlatformDelegateChromeViews::SetBrowserView(
CefRefPtr<CefBrowserView> browser_view) { CefRefPtr<CefBrowserView> browser_view) {
DCHECK(!browser_view_); DCHECK(!browser_view_);
DCHECK(browser_view); DCHECK(browser_view);
browser_view_ = static_cast<CefBrowserViewImpl*>(browser_view.get()); browser_view_ =
static_cast<CefBrowserViewImpl*>(browser_view.get())->GetWeakPtr();
} }
void CefBrowserPlatformDelegateChromeViews::WebContentsCreated( void CefBrowserPlatformDelegateChromeViews::WebContentsCreated(
@ -37,8 +38,11 @@ void CefBrowserPlatformDelegateChromeViews::WebContentsCreated(
void CefBrowserPlatformDelegateChromeViews::WebContentsDestroyed( void CefBrowserPlatformDelegateChromeViews::WebContentsDestroyed(
content::WebContents* web_contents) { content::WebContents* web_contents) {
CefBrowserPlatformDelegateChrome::WebContentsDestroyed(web_contents); CefBrowserPlatformDelegateChrome::WebContentsDestroyed(web_contents);
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->WebContentsDestroyed(web_contents); browser_view_->WebContentsDestroyed(web_contents);
} }
}
void CefBrowserPlatformDelegateChromeViews::BrowserCreated( void CefBrowserPlatformDelegateChromeViews::BrowserCreated(
CefBrowserHostBase* browser) { CefBrowserHostBase* browser) {
@ -48,7 +52,7 @@ void CefBrowserPlatformDelegateChromeViews::BrowserCreated(
void CefBrowserPlatformDelegateChromeViews::NotifyBrowserCreated() { void CefBrowserPlatformDelegateChromeViews::NotifyBrowserCreated() {
if (auto delegate = browser_view_->delegate()) { if (auto delegate = browser_view_->delegate()) {
delegate->OnBrowserCreated(browser_view_, browser_.get()); delegate->OnBrowserCreated(browser_view_.get(), browser_.get());
// DevTools windows hide the notification bubble by default. However, we // DevTools windows hide the notification bubble by default. However, we
// don't currently have the ability to intercept WebContents creation via // don't currently have the ability to intercept WebContents creation via
@ -75,8 +79,9 @@ void CefBrowserPlatformDelegateChromeViews::NotifyBrowserCreated() {
} }
void CefBrowserPlatformDelegateChromeViews::NotifyBrowserDestroyed() { void CefBrowserPlatformDelegateChromeViews::NotifyBrowserDestroyed() {
if (browser_view_->delegate()) { // |browser_view_| may be destroyed before this callback arrives.
browser_view_->delegate()->OnBrowserDestroyed(browser_view_, if (browser_view_ && browser_view_->delegate()) {
browser_view_->delegate()->OnBrowserDestroyed(browser_view_.get(),
browser_.get()); browser_.get());
} }
} }
@ -84,8 +89,12 @@ void CefBrowserPlatformDelegateChromeViews::NotifyBrowserDestroyed() {
void CefBrowserPlatformDelegateChromeViews::BrowserDestroyed( void CefBrowserPlatformDelegateChromeViews::BrowserDestroyed(
CefBrowserHostBase* browser) { CefBrowserHostBase* browser) {
CefBrowserPlatformDelegateChrome::BrowserDestroyed(browser); CefBrowserPlatformDelegateChrome::BrowserDestroyed(browser);
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->BrowserDestroyed(browser); browser_view_->BrowserDestroyed(browser);
} }
browser_view_ = nullptr;
}
void CefBrowserPlatformDelegateChromeViews::CloseHostWindow() { void CefBrowserPlatformDelegateChromeViews::CloseHostWindow() {
views::Widget* widget = GetWindowWidget(); views::Widget* widget = GetWindowWidget();
@ -100,7 +109,7 @@ CefWindowHandle CefBrowserPlatformDelegateChromeViews::GetHostWindowHandle()
} }
views::Widget* CefBrowserPlatformDelegateChromeViews::GetWindowWidget() const { views::Widget* CefBrowserPlatformDelegateChromeViews::GetWindowWidget() const {
if (browser_view_->root_view()) { if (browser_view_ && browser_view_->root_view()) {
return browser_view_->root_view()->GetWidget(); return browser_view_->root_view()->GetWidget();
} }
return nullptr; return nullptr;

View File

@ -5,6 +5,7 @@
#ifndef CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_ #ifndef CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_
#define CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_ #define CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_
#include "base/memory/weak_ptr.h"
#include "cef/libcef/browser/chrome/browser_platform_delegate_chrome.h" #include "cef/libcef/browser/chrome/browser_platform_delegate_chrome.h"
#include "cef/libcef/browser/views/browser_view_impl.h" #include "cef/libcef/browser/views/browser_view_impl.h"
@ -33,12 +34,17 @@ class CefBrowserPlatformDelegateChromeViews
void SetBrowserView(CefRefPtr<CefBrowserView> browser_view) override; void SetBrowserView(CefRefPtr<CefBrowserView> browser_view) override;
bool IsViewsHosted() const override; bool IsViewsHosted() const override;
CefRefPtr<CefBrowserViewImpl> browser_view() const { return browser_view_; } CefBrowserViewImpl* browser_view() const { return browser_view_.get(); }
private: private:
CefWindowImpl* GetWindowImpl() const; CefWindowImpl* GetWindowImpl() const;
CefRefPtr<CefBrowserViewImpl> browser_view_; // Holding a weak reference here because we want the CefBrowserViewImpl to be
// destroyed first if all references are released by the client.
// CefBrowserViewImpl destruction will then trigger destruction of any
// associated CefBrowserHostBase (which owns this CefBrowserPlatformDelegate
// object).
base::WeakPtr<CefBrowserViewImpl> browser_view_;
}; };
#endif // CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_ #endif // CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_VIEWS_H_

View File

@ -11,6 +11,13 @@ ChromeBrowserView::ChromeBrowserView(CefBrowserViewImpl* cef_browser_view)
: ParentClass(cef_browser_view->delegate()), : ParentClass(cef_browser_view->delegate()),
cef_browser_view_(cef_browser_view) {} cef_browser_view_(cef_browser_view) {}
ChromeBrowserView::~ChromeBrowserView() {
if (cef_toolbar_) {
WillDestroyToolbar();
cef_toolbar_ = nullptr;
}
}
void ChromeBrowserView::InitBrowser(std::unique_ptr<Browser> browser) { void ChromeBrowserView::InitBrowser(std::unique_ptr<Browser> browser) {
DCHECK(!web_view_); DCHECK(!web_view_);

View File

@ -30,6 +30,7 @@ class ChromeBrowserView
// |cef_browser_view| is non-nullptr and will outlive this object. // |cef_browser_view| is non-nullptr and will outlive this object.
explicit ChromeBrowserView(CefBrowserViewImpl* cef_browser_view); explicit ChromeBrowserView(CefBrowserViewImpl* cef_browser_view);
~ChromeBrowserView() override;
ChromeBrowserView(const ChromeBrowserView&) = delete; ChromeBrowserView(const ChromeBrowserView&) = delete;
ChromeBrowserView& operator=(const ChromeBrowserView&) = delete; ChromeBrowserView& operator=(const ChromeBrowserView&) = delete;

View File

@ -449,9 +449,6 @@ void CefMainRunner::StartShutdownOnUIThread(
content::BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting( content::BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
content::BrowserThread::IO); content::BrowserThread::IO);
static_cast<content::ContentMainRunnerImpl*>(main_runner_.get())
->ShutdownOnUIThread();
std::move(shutdown_on_ui_thread).Run(); std::move(shutdown_on_ui_thread).Run();
BeforeUIThreadShutdown(); BeforeUIThreadShutdown();
} }
@ -462,6 +459,9 @@ void CefMainRunner::FinishShutdownOnUIThread() {
// It is safe to call multiple times. // It is safe to call multiple times.
ChromeProcessSingleton::DeleteInstance(); ChromeProcessSingleton::DeleteInstance();
} }
static_cast<content::ContentMainRunnerImpl*>(main_runner_.get())
->ShutdownOnUIThread();
} }
void CefMainRunner::BeforeUIThreadInitialize() { void CefMainRunner::BeforeUIThreadInitialize() {

View File

@ -27,7 +27,8 @@ void CefBrowserPlatformDelegateViews::SetBrowserView(
CefRefPtr<CefBrowserView> browser_view) { CefRefPtr<CefBrowserView> browser_view) {
DCHECK(!browser_view_); DCHECK(!browser_view_);
DCHECK(browser_view); DCHECK(browser_view);
browser_view_ = static_cast<CefBrowserViewImpl*>(browser_view.get()); browser_view_ =
static_cast<CefBrowserViewImpl*>(browser_view.get())->GetWeakPtr();
} }
void CefBrowserPlatformDelegateViews::WebContentsCreated( void CefBrowserPlatformDelegateViews::WebContentsCreated(
@ -41,7 +42,10 @@ void CefBrowserPlatformDelegateViews::WebContentsCreated(
void CefBrowserPlatformDelegateViews::WebContentsDestroyed( void CefBrowserPlatformDelegateViews::WebContentsDestroyed(
content::WebContents* web_contents) { content::WebContents* web_contents) {
CefBrowserPlatformDelegateAlloy::WebContentsDestroyed(web_contents); CefBrowserPlatformDelegateAlloy::WebContentsDestroyed(web_contents);
browser_view_->WebContentsCreated(web_contents); // |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->WebContentsDestroyed(web_contents);
}
native_delegate_->WebContentsDestroyed(web_contents); native_delegate_->WebContentsDestroyed(web_contents);
} }
@ -57,15 +61,16 @@ void CefBrowserPlatformDelegateViews::NotifyBrowserCreated() {
DCHECK(browser_view_); DCHECK(browser_view_);
DCHECK(browser_); DCHECK(browser_);
if (browser_view_->delegate()) { if (browser_view_->delegate()) {
browser_view_->delegate()->OnBrowserCreated(browser_view_, browser_.get()); browser_view_->delegate()->OnBrowserCreated(browser_view_.get(),
browser_.get());
} }
} }
void CefBrowserPlatformDelegateViews::NotifyBrowserDestroyed() { void CefBrowserPlatformDelegateViews::NotifyBrowserDestroyed() {
DCHECK(browser_view_);
DCHECK(browser_); DCHECK(browser_);
if (browser_view_->delegate()) { // |browser_view_| may be destroyed before this callback arrives.
browser_view_->delegate()->OnBrowserDestroyed(browser_view_, if (browser_view_ && browser_view_->delegate()) {
browser_view_->delegate()->OnBrowserDestroyed(browser_view_.get(),
browser_.get()); browser_.get());
} }
} }
@ -74,7 +79,10 @@ void CefBrowserPlatformDelegateViews::BrowserDestroyed(
CefBrowserHostBase* browser) { CefBrowserHostBase* browser) {
CefBrowserPlatformDelegateAlloy::BrowserDestroyed(browser); CefBrowserPlatformDelegateAlloy::BrowserDestroyed(browser);
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->BrowserDestroyed(browser); browser_view_->BrowserDestroyed(browser);
}
browser_view_ = nullptr; browser_view_ = nullptr;
native_delegate_->BrowserDestroyed(browser); native_delegate_->BrowserDestroyed(browser);
} }

View File

@ -5,6 +5,7 @@
#ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_ #ifndef CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_
#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_ #define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_
#include "base/memory/weak_ptr.h"
#include "cef/libcef/browser/alloy/browser_platform_delegate_alloy.h" #include "cef/libcef/browser/alloy/browser_platform_delegate_alloy.h"
#include "cef/libcef/browser/native/browser_platform_delegate_native.h" #include "cef/libcef/browser/native/browser_platform_delegate_native.h"
#include "cef/libcef/browser/views/browser_view_impl.h" #include "cef/libcef/browser/views/browser_view_impl.h"
@ -65,7 +66,13 @@ class CefBrowserPlatformDelegateViews
private: private:
std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_; std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_;
CefRefPtr<CefBrowserViewImpl> browser_view_;
// Holding a weak reference here because we want the CefBrowserViewImpl to be
// destroyed first if all references are released by the client.
// CefBrowserViewImpl destruction will then trigger destruction of any
// associated CefBrowserHostBase (which owns this CefBrowserPlatformDelegate
// object).
base::WeakPtr<CefBrowserViewImpl> browser_view_;
}; };
#endif // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_ #endif // CEF_LIBCEF_BROWSER_VIEWS_BROWSER_PLATFORM_DELEGATE_VIEWS_H_

View File

@ -35,30 +35,39 @@ std::optional<cef_gesture_command_t> GetGestureCommand(
return std::nullopt; return std::nullopt;
} }
bool ComputeAlloyStyle(CefBrowserViewDelegate* cef_delegate, bool ComputeAlloyStyle(
bool is_devtools_popup) { CefBrowserViewDelegate* cef_delegate,
bool is_devtools_popup,
std::optional<cef_runtime_style_t> opener_runtime_style) {
if (is_devtools_popup) {
// Alloy style is not supported with Chrome DevTools popups. // Alloy style is not supported with Chrome DevTools popups.
const bool supports_alloy_style = !is_devtools_popup; if (cef_delegate &&
const auto default_style = CEF_RUNTIME_STYLE_CHROME; cef_delegate->GetBrowserRuntimeStyle() == CEF_RUNTIME_STYLE_ALLOY) {
auto result_style = default_style;
if (cef_delegate) {
auto requested_style = cef_delegate->GetBrowserRuntimeStyle();
if (requested_style == CEF_RUNTIME_STYLE_ALLOY) {
if (supports_alloy_style) {
result_style = requested_style;
} else {
LOG(ERROR) << "GetBrowserRuntimeStyle() requested Alloy style; only " LOG(ERROR) << "GetBrowserRuntimeStyle() requested Alloy style; only "
"Chrome style is supported"; "Chrome style is supported for DevTools popups";
}
} else if (requested_style == CEF_RUNTIME_STYLE_CHROME) {
// Chrome style is always supported.
result_style = requested_style;
} }
return false;
} }
return result_style == CEF_RUNTIME_STYLE_ALLOY; if (opener_runtime_style) {
// Popup style must match the opener style.
const bool opener_alloy_style =
*opener_runtime_style == CEF_RUNTIME_STYLE_ALLOY;
if (cef_delegate) {
const auto requested_style = cef_delegate->GetBrowserRuntimeStyle();
if (requested_style != CEF_RUNTIME_STYLE_DEFAULT &&
requested_style != (opener_alloy_style ? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME)) {
LOG(ERROR)
<< "GetBrowserRuntimeStyle() for popups must match opener style";
}
}
return opener_alloy_style;
}
// Chrome style is the default unless Alloy is specifically requested.
return cef_delegate &&
cef_delegate->GetBrowserRuntimeStyle() == CEF_RUNTIME_STYLE_ALLOY;
} }
} // namespace } // namespace
@ -111,7 +120,8 @@ CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::Create(
} }
CefRefPtr<CefBrowserViewImpl> browser_view = CefRefPtr<CefBrowserViewImpl> browser_view =
new CefBrowserViewImpl(delegate, /*is_devtools_popup=*/false); new CefBrowserViewImpl(delegate, /*is_devtools_popup=*/false,
/*opener_runtime_style=*/std::nullopt);
browser_view->SetPendingBrowserCreateParams( browser_view->SetPendingBrowserCreateParams(
window_info, client, url, settings, extra_info, request_context); window_info, client, url, settings, extra_info, request_context);
browser_view->Initialize(); browser_view->Initialize();
@ -123,16 +133,38 @@ CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::Create(
CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::CreateForPopup( CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::CreateForPopup(
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
CefRefPtr<CefBrowserViewDelegate> delegate, CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools) { bool is_devtools,
cef_runtime_style_t opener_runtime_style) {
CEF_REQUIRE_UIT_RETURN(nullptr); CEF_REQUIRE_UIT_RETURN(nullptr);
CefRefPtr<CefBrowserViewImpl> browser_view = CefRefPtr<CefBrowserViewImpl> browser_view =
new CefBrowserViewImpl(delegate, is_devtools); new CefBrowserViewImpl(delegate, is_devtools, opener_runtime_style);
browser_view->Initialize(); browser_view->Initialize();
browser_view->SetDefaults(settings); browser_view->SetDefaults(settings);
return browser_view; return browser_view;
} }
CefBrowserViewImpl::~CefBrowserViewImpl() {
// We want no further callbacks to this object.
weak_ptr_factory_.InvalidateWeakPtrs();
// |browser_| may exist here if the BrowserView was removed from the Views
// hierarchy prior to tear-down and the last BrowserView reference was
// released. In that case DisassociateFromWidget() will be called when the
// BrowserView is removed from the Window but Detach() will not be called
// because the BrowserView was not destroyed via the Views hierarchy
// tear-down.
DCHECK(!cef_widget_);
if (browser_ && !browser_->WillBeDestroyed()) {
// With Alloy style |browser_| will disappear when WindowDestroyed()
// indirectly calls BrowserDestroyed() so keep a reference.
CefRefPtr<CefBrowserHostBase> browser = browser_;
// Force the browser to be destroyed.
browser->WindowDestroyed();
}
}
void CefBrowserViewImpl::WebContentsCreated( void CefBrowserViewImpl::WebContentsCreated(
content::WebContents* web_contents) { content::WebContents* web_contents) {
if (web_view()) { if (web_view()) {
@ -269,8 +301,8 @@ void CefBrowserViewImpl::AddedToWidget() {
CefWidget* cef_widget = CefWidget::GetForWidget(widget); CefWidget* cef_widget = CefWidget::GetForWidget(widget);
DCHECK(cef_widget); DCHECK(cef_widget);
if (!browser_) { if (!browser_ && !is_alloy_style_) {
if (cef_widget->IsAlloyStyle() && !is_alloy_style_) { if (cef_widget->IsAlloyStyle()) {
LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window"; LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window";
return; return;
} }
@ -337,9 +369,12 @@ bool CefBrowserViewImpl::OnGestureEvent(ui::GestureEvent* event) {
CefBrowserViewImpl::CefBrowserViewImpl( CefBrowserViewImpl::CefBrowserViewImpl(
CefRefPtr<CefBrowserViewDelegate> delegate, CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools_popup) bool is_devtools_popup,
std::optional<cef_runtime_style_t> opener_runtime_style)
: ParentClass(delegate), : ParentClass(delegate),
is_alloy_style_(ComputeAlloyStyle(delegate.get(), is_devtools_popup)), is_alloy_style_(ComputeAlloyStyle(delegate.get(),
is_devtools_popup,
opener_runtime_style)),
weak_ptr_factory_(this) {} weak_ptr_factory_(this) {}
void CefBrowserViewImpl::SetPendingBrowserCreateParams( void CefBrowserViewImpl::SetPendingBrowserCreateParams(

View File

@ -6,6 +6,8 @@
#define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_IMPL_H_ #define CEF_LIBCEF_BROWSER_VIEWS_BROWSER_VIEW_IMPL_H_
#pragma once #pragma once
#include <optional>
#include "base/functional/callback_forward.h" #include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
@ -33,6 +35,8 @@ class CefBrowserViewImpl
CefBrowserViewImpl(const CefBrowserViewImpl&) = delete; CefBrowserViewImpl(const CefBrowserViewImpl&) = delete;
CefBrowserViewImpl& operator=(const CefBrowserViewImpl&) = delete; CefBrowserViewImpl& operator=(const CefBrowserViewImpl&) = delete;
~CefBrowserViewImpl() override;
// Create a new CefBrowserView instance. |delegate| may be nullptr. // Create a new CefBrowserView instance. |delegate| may be nullptr.
// |window_info| will only be used when creating a Chrome child window. // |window_info| will only be used when creating a Chrome child window.
static CefRefPtr<CefBrowserViewImpl> Create( static CefRefPtr<CefBrowserViewImpl> Create(
@ -49,7 +53,8 @@ class CefBrowserViewImpl
static CefRefPtr<CefBrowserViewImpl> CreateForPopup( static CefRefPtr<CefBrowserViewImpl> CreateForPopup(
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
CefRefPtr<CefBrowserViewDelegate> delegate, CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools); bool is_devtools,
cef_runtime_style_t opener_runtime_style);
// Called from CefBrowserPlatformDelegate[Chrome]Views. // Called from CefBrowserPlatformDelegate[Chrome]Views.
void WebContentsCreated(content::WebContents* web_contents); void WebContentsCreated(content::WebContents* web_contents);
@ -96,12 +101,17 @@ class CefBrowserViewImpl
bool IsAlloyStyle() const { return is_alloy_style_; } bool IsAlloyStyle() const { return is_alloy_style_; }
bool IsChromeStyle() const { return !is_alloy_style_; } bool IsChromeStyle() const { return !is_alloy_style_; }
base::WeakPtr<CefBrowserViewImpl> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private: private:
// Create a new implementation object. // Create a new implementation object.
// Always call Initialize() after creation. // Always call Initialize() after creation.
// |delegate| may be nullptr. // |delegate| may be nullptr.
CefBrowserViewImpl(CefRefPtr<CefBrowserViewDelegate> delegate, CefBrowserViewImpl(CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools_popup); bool is_devtools_popup,
std::optional<cef_runtime_style_t> opener_runtime_style);
void SetPendingBrowserCreateParams( void SetPendingBrowserCreateParams(
const CefWindowInfo& window_info, const CefWindowInfo& window_info,

View File

@ -52,6 +52,7 @@ class CefNativeWidgetMac : public views::NativeWidgetMac {
bool IsCefWindowInitialized() const; bool IsCefWindowInitialized() const;
raw_ptr<BrowserView, AcrossTasksDanglingUntriaged> browser_view_ = nullptr; raw_ptr<BrowserView, AcrossTasksDanglingUntriaged> browser_view_ = nullptr;
bool initialized_ = false;
}; };
#endif // CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_ #endif // CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_

View File

@ -138,7 +138,12 @@ void CefNativeWidgetMac::OnWindowFullscreenTransitionComplete() {
} }
void CefNativeWidgetMac::OnWindowInitialized() { void CefNativeWidgetMac::OnWindowInitialized() {
if (!browser_view_) { // This connects the native widget with the command dispatcher so accelerators
// work even if a browser_view_ is not created later.
// The initialized_ check is necessary because the method can be called twice:
// 1. From NativeWidgetMac::InitNativeWidget
// 2. From ChromeBrowserFrame::Init
if (initialized_) {
return; return;
} }
@ -146,10 +151,12 @@ void CefNativeWidgetMac::OnWindowInitialized() {
if (auto* bridge = GetInProcessNSWindowBridge()) { if (auto* bridge = GetInProcessNSWindowBridge()) {
bridge->SetCommandDispatcher([[ChromeCommandDispatcherDelegate alloc] init], bridge->SetCommandDispatcher([[ChromeCommandDispatcherDelegate alloc] init],
[[BrowserWindowCommandHandler alloc] init]); [[BrowserWindowCommandHandler alloc] init]);
} else { initialized_ = true;
} else if (browser_view_) {
if (auto* host = GetHostForBrowser(browser_view_->browser())) { if (auto* host = GetHostForBrowser(browser_view_->browser())) {
host->GetAppShim()->CreateCommandDispatcherForWidget( host->GetAppShim()->CreateCommandDispatcherForWidget(
GetNSWindowHost()->bridged_native_widget_id()); GetNSWindowHost()->bridged_native_widget_id());
initialized_ = true;
} }
} }
} }

View File

@ -280,18 +280,17 @@ void CefOverlayViewHost::SetOverlayBounds(const gfx::Rect& bounds) {
return; return;
} }
gfx::Rect new_bounds = bounds; // Empty bounds are not allowed.
if (bounds.IsEmpty()) {
// Keep the result inside the widget.
new_bounds.Intersect(window_view_->bounds());
if (new_bounds == bounds_) {
return; return;
} }
bounds_changing_ = true; bounds_changing_ = true;
bounds_ = bounds;
// Keep the result inside the widget.
bounds_.Intersect(window_view_->bounds());
bounds_ = new_bounds;
if (view_->size() != bounds_.size()) { if (view_->size() != bounds_.size()) {
view_->SetSize(bounds_.size()); view_->SetSize(bounds_.size());
} }
@ -313,6 +312,11 @@ void CefOverlayViewHost::OnViewBoundsChanged(views::View* observed_view) {
MoveIfNecessary(); MoveIfNecessary();
} }
void CefOverlayViewHost::OnViewIsDeleting(views::View* observed_view) {
view_ = nullptr;
Cleanup();
}
gfx::Rect CefOverlayViewHost::ComputeBounds() const { gfx::Rect CefOverlayViewHost::ComputeBounds() const {
// This method is only used with corner docking. // This method is only used with corner docking.
DCHECK_NE(docking_mode_, CEF_DOCKING_MODE_CUSTOM); DCHECK_NE(docking_mode_, CEF_DOCKING_MODE_CUSTOM);

View File

@ -44,6 +44,7 @@ class CefOverlayViewHost : public views::WidgetDelegate,
// views::ViewObserver methods: // views::ViewObserver methods:
void OnViewBoundsChanged(views::View* observed_view) override; void OnViewBoundsChanged(views::View* observed_view) override;
void OnViewIsDeleting(views::View* observed_view) override;
cef_docking_mode_t docking_mode() const { return docking_mode_; } cef_docking_mode_t docking_mode() const { return docking_mode_; }
CefRefPtr<CefOverlayController> controller() const { return cef_controller_; } CefRefPtr<CefOverlayController> controller() const { return cef_controller_; }

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

@ -4,6 +4,7 @@
#include "include/wrapper/cef_message_router.h" #include "include/wrapper/cef_message_router.h"
#include <algorithm>
#include <limits> #include <limits>
#include <map> #include <map>
#include <set> #include <set>

View File

@ -4,8 +4,16 @@
#include "tests/cefclient/browser/default_client_handler.h" #include "tests/cefclient/browser/default_client_handler.h"
#include "tests/cefclient/browser/main_context.h"
#include "tests/cefclient/browser/root_window_manager.h"
namespace client { namespace client {
DefaultClientHandler::DefaultClientHandler(std::optional<bool> use_alloy_style)
: use_alloy_style_(
use_alloy_style.value_or(MainContext::Get()->UseAlloyStyleGlobal())) {
}
// static // static
CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient( CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
CefRefPtr<CefClient> client) { CefRefPtr<CefClient> client) {
@ -16,4 +24,39 @@ CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
return nullptr; return nullptr;
} }
bool DefaultClientHandler::OnBeforePopup(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
CefLifeSpanHandler::WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
CefRefPtr<CefDictionaryValue>& extra_info,
bool* no_javascript_access) {
CEF_REQUIRE_UI_THREAD();
if (target_disposition == CEF_WOD_NEW_PICTURE_IN_PICTURE) {
// Use default handling for document picture-in-picture popups.
client = nullptr;
return false;
}
// Used to configure default values.
RootWindowConfig config(/*command_line=*/nullptr);
// Potentially create a new RootWindow for the popup browser that will be
// created asynchronously.
MainContext::Get()->GetRootWindowManager()->CreateRootWindowAsPopup(
config.use_views, use_alloy_style_, config.with_controls,
/*is_osr=*/false, /*is_devtools=*/false, popupFeatures, windowInfo,
client, settings);
// Allow popup creation.
return false;
}
} // namespace client } // namespace client

View File

@ -6,6 +6,8 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_ #define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_
#pragma once #pragma once
#include <optional>
#include "tests/cefclient/browser/base_client_handler.h" #include "tests/cefclient/browser/base_client_handler.h"
namespace client { namespace client {
@ -14,18 +16,37 @@ namespace client {
// style only. // style only.
class DefaultClientHandler : public BaseClientHandler { class DefaultClientHandler : public BaseClientHandler {
public: public:
DefaultClientHandler() = default; // If |use_alloy_style| is nullopt the global default will be used.
explicit DefaultClientHandler(
std::optional<bool> use_alloy_style = std::nullopt);
// Returns the DefaultClientHandler for |client|, or nullptr if |client| is // Returns the DefaultClientHandler for |client|, or nullptr if |client| is
// not a DefaultClientHandler. // not a DefaultClientHandler.
static CefRefPtr<DefaultClientHandler> GetForClient( static CefRefPtr<DefaultClientHandler> GetForClient(
CefRefPtr<CefClient> client); CefRefPtr<CefClient> client);
protected:
bool OnBeforePopup(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
CefLifeSpanHandler::WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
CefRefPtr<CefDictionaryValue>& extra_info,
bool* no_javascript_access) override;
private: private:
// Used to determine the object type. // Used to determine the object type.
virtual const void* GetTypeKey() const override { return &kTypeKey; } virtual const void* GetTypeKey() const override { return &kTypeKey; }
static constexpr int kTypeKey = 0; static constexpr int kTypeKey = 0;
const bool use_alloy_style_;
IMPLEMENT_REFCOUNTING(DefaultClientHandler); IMPLEMENT_REFCOUNTING(DefaultClientHandler);
DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler); DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler);
}; };

View File

@ -21,6 +21,7 @@
#define IDC_NAV_STOP 203 #define IDC_NAV_STOP 203
#define ID_QUIT 32500 #define ID_QUIT 32500
#define ID_FIND 32501 #define ID_FIND 32501
#define ID_POPOUT_OVERLAY 32502
#define ID_TESTS_FIRST 32700 #define ID_TESTS_FIRST 32700
#define ID_TESTS_GETSOURCE 32700 #define ID_TESTS_GETSOURCE 32700
#define ID_TESTS_GETTEXT 32701 #define ID_TESTS_GETTEXT 32701

View File

@ -134,8 +134,11 @@ scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) { if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) {
// Use default window creation for the popup. A new |client| instance is // Use default window creation for the popup. A new |client| instance is
// still required by cefclient architecture. // required by cefclient architecture if the type is not already
client = new DefaultClientHandler(); // DefaultClientHandler.
if (!DefaultClientHandler::GetForClient(client)) {
client = new DefaultClientHandler(use_alloy_style);
}
return nullptr; return nullptr;
} }

View File

@ -0,0 +1,315 @@
// 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/views_overlay_browser.h"
#include "include/views/cef_window.h"
#include "tests/cefclient/browser/resource.h"
#include "tests/cefclient/browser/views_window.h"
namespace client {
namespace {
void AddPopOutAccelerator(CefRefPtr<CefWindow> window) {
// Add an accelerator to toggle the BrowserView popout. OnAccelerator will be
// called when the accelerator is triggered.
window->SetAccelerator(ID_POPOUT_OVERLAY, 'O', /*shift_pressed=*/false,
/*ctrl_pressed=*/false, /*alt_pressed=*/true,
/*high_priority=*/true);
}
// Popout window delegate implementation.
class PopoutWindowDelegate : public CefWindowDelegate {
public:
PopoutWindowDelegate(base::WeakPtr<ViewsOverlayBrowser> overlay,
CefRefPtr<CefBrowserView> browser_view)
: overlay_(overlay), browser_view_(browser_view) {}
PopoutWindowDelegate(const PopoutWindowDelegate&) = delete;
PopoutWindowDelegate& operator=(const PopoutWindowDelegate&) = delete;
static PopoutWindowDelegate* GetForWindow(CefRefPtr<CefWindow> window) {
return static_cast<PopoutWindowDelegate*>(window->GetDelegate().get());
}
void OnWindowCreated(CefRefPtr<CefWindow> window) override {
window->AddChildView(browser_view_);
window->Show();
// Add the popout accelerator to the popout Window.
AddPopOutAccelerator(window);
browser_view_->RequestFocus();
}
bool CanClose(CefRefPtr<CefWindow> window) override {
CefRefPtr<CefBrowser> browser =
browser_view_ ? browser_view_->GetBrowser() : nullptr;
if (overlay_ && browser && !browser->GetHost()->IsReadyToBeClosed()) {
// Proceed with the window close, but don't close the browser. The browser
// will be returned to the overlay in OnWindowClosing().
return_to_overlay_ = true;
return true;
}
if (browser) {
// We must close the browser, either because the popout Window is the
// final owner of the BrowserView, or because the browser is ready to be
// closed internally (e.g. `window.close()` was called).
return browser->GetHost()->TryCloseBrowser();
}
return true;
}
void OnWindowClosing(CefRefPtr<CefWindow> window) override {
if (overlay_ && return_to_overlay_) {
// Give the browser back to the overlay.
overlay_->ToggleBrowserView();
}
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
if (overlay_) {
overlay_->PopOutWindowDestroyed();
overlay_ = nullptr;
}
browser_view_ = nullptr;
}
cef_runtime_style_t GetWindowRuntimeStyle() override {
return browser_view_->GetRuntimeStyle();
}
bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) override {
if (overlay_) {
return overlay_->OnAccelerator(window, command_id);
}
return false;
}
[[nodiscard]] CefRefPtr<CefBrowserView> DetachBrowserView() {
overlay_ = nullptr;
auto browser_view = browser_view_;
browser_view_ = nullptr;
return browser_view;
}
void OverlayDestroyed() { overlay_ = nullptr; }
private:
base::WeakPtr<ViewsOverlayBrowser> overlay_;
CefRefPtr<CefBrowserView> browser_view_;
bool return_to_overlay_ = false;
IMPLEMENT_REFCOUNTING(PopoutWindowDelegate);
};
} // namespace
ViewsOverlayBrowser::ViewsOverlayBrowser(ViewsWindow* owner_window)
: owner_window_(owner_window) {}
void ViewsOverlayBrowser::Initialize(
CefRefPtr<CefWindow> window,
CefRefPtr<CefClient> client,
const std::string& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> request_context) {
CHECK(!window_);
window_ = window;
CHECK(window_);
// Add the accelerator to the main window.
AddPopOutAccelerator(window_);
browser_view_ = CefBrowserView::CreateBrowserView(
client, url, settings, nullptr, request_context, this);
CHECK(browser_view_);
// Add the BrowserView to an overlay in the main window.
controller_ = window_->AddOverlayView(browser_view_, CEF_DOCKING_MODE_CUSTOM,
/*can_activate=*/true);
CHECK(controller_);
}
void ViewsOverlayBrowser::Destroy() {
window_ = nullptr;
if (popout_window_) {
// The BrowserView is popped out, and the main Window is closed first.
// Let the popout Window handle BrowserView destruction.
PopoutWindowDelegate::GetForWindow(popout_window_)->OverlayDestroyed();
popout_window_->Close();
popout_window_ = nullptr;
}
if (controller_) {
if (controller_->IsValid()) {
controller_->Destroy();
}
controller_ = nullptr;
owner_window_->UpdateDraggableRegions();
}
if (browser_view_) {
// We hold the last reference to the BrowserView, and releasing it will
// trigger overlay Browser destruction. OnBeforeClose for that Browser may
// be called synchronously or asynchronously depending on whether
// beforeunload needs to be dispatched.
DCHECK(browser_view_->HasOneRef());
browser_view_ = nullptr;
}
}
bool ViewsOverlayBrowser::IsValid() const {
// Intentionally not checking |popout_window_->IsValid()| here because the
// pop-in behavior will be triggered by |popout_window_| closing.
return (controller_ && controller_->IsValid()) || popout_window_;
}
void ViewsOverlayBrowser::ToggleBrowserView() {
if (browser_view_) {
PopOutBrowserView();
} else {
PopInBrowserView();
}
owner_window_->UpdateDraggableRegions();
}
void ViewsOverlayBrowser::PopOutBrowserView() {
CHECK(browser_view_);
DCHECK(controller_ && controller_->IsValid());
controller_->Destroy();
controller_ = nullptr;
// We hold the only reference to the BrowserView.
DCHECK(browser_view_->HasOneRef());
// Create a new popout Window and pass ownership of the BrowserView.
CHECK(!popout_window_);
popout_window_ = CefWindow::CreateTopLevelWindow(
new PopoutWindowDelegate(weak_ptr_factory_.GetWeakPtr(), browser_view_));
browser_view_ = nullptr;
}
void ViewsOverlayBrowser::PopInBrowserView() {
CHECK(!browser_view_);
// Resume ownership of the BrowserView and close the popout Window.
CHECK(popout_window_);
browser_view_ =
PopoutWindowDelegate::GetForWindow(popout_window_)->DetachBrowserView();
popout_window_->RemoveChildView(browser_view_);
popout_window_->Close();
popout_window_ = nullptr;
// We hold the only reference to the BrowserView.
DCHECK(browser_view_->HasOneRef());
// Add the BrowserView to an overlay in the main window.
controller_ = window_->AddOverlayView(browser_view_, CEF_DOCKING_MODE_CUSTOM,
/*can_activate=*/true);
CHECK(controller_);
// Make sure the overlay is positioned correctly.
UpdateBounds(last_insets_);
}
void ViewsOverlayBrowser::UpdateBounds(CefInsets insets) {
last_insets_ = insets;
if (!controller_) {
return;
}
// Update location bar size, position and visibility.
const auto window_bounds = window_->GetBounds();
// Client coordinates with insets.
CefRect bounds;
bounds.x = insets.left;
bounds.width = window_bounds.width - insets.left - insets.right;
bounds.y = insets.top;
bounds.height = window_bounds.height - insets.top - insets.bottom;
const auto min_size = browser_view_->GetMinimumSize();
if (bounds.width < min_size.width || bounds.height < min_size.height) {
// Not enough space.
controller_->SetVisible(false);
} else {
controller_->SetSize(CefSize(bounds.width, bounds.height));
controller_->SetBounds(bounds);
controller_->SetVisible(true);
}
}
void ViewsOverlayBrowser::UpdateDraggableRegions(
std::vector<CefDraggableRegion>& window_regions) {
if (controller_ && controller_->IsVisible()) {
window_regions.emplace_back(controller_->GetBounds(),
/*draggable=*/false);
}
}
bool ViewsOverlayBrowser::OnAccelerator(CefRefPtr<CefWindow> window,
int command_id) {
if (IsValid() && command_id == ID_POPOUT_OVERLAY) {
ToggleBrowserView();
return true;
}
return false;
}
void ViewsOverlayBrowser::PopOutWindowDestroyed() {
popout_window_ = nullptr;
}
CefSize ViewsOverlayBrowser::GetMinimumSize(CefRefPtr<CefView> view) {
return CefSize(200, 200);
}
void ViewsOverlayBrowser::OnBrowserDestroyed(
CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowser> browser) {
// Might be popped out currently.
if (!controller_) {
return;
}
// Destroy the overlay controller if the browser is destroyed first (e.g. via
// `window.close()`).
controller_->Destroy();
controller_ = nullptr;
owner_window_->UpdateDraggableRegions();
}
CefRefPtr<CefBrowserViewDelegate>
ViewsOverlayBrowser::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) {
return owner_window_->GetDelegateForPopupBrowserView(browser_view, settings,
client, is_devtools);
}
bool ViewsOverlayBrowser::OnPopupBrowserViewCreated(
CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) {
return owner_window_->OnPopupBrowserViewCreated(
browser_view, popup_browser_view, is_devtools);
}
cef_runtime_style_t ViewsOverlayBrowser::GetBrowserRuntimeStyle() {
// Overlay browser view must always be Alloy style.
return CEF_RUNTIME_STYLE_ALLOY;
}
} // namespace client

View File

@ -0,0 +1,86 @@
// 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_VIEWS_OVERLAY_BROWSER_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_
#pragma once
#include "include/base/cef_weak_ptr.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_browser_view_delegate.h"
#include "include/views/cef_overlay_controller.h"
namespace client {
class ViewsWindow;
// Implements a browser view that receives absolute positioning on top of the
// main browser view. All methods must be called on the browser process UI
// thread.
class ViewsOverlayBrowser : public CefBrowserViewDelegate {
public:
explicit ViewsOverlayBrowser(ViewsWindow* owner_window);
void Initialize(CefRefPtr<CefWindow> window,
CefRefPtr<CefClient> client,
const std::string& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> request_context);
void Destroy();
bool IsValid() const;
// Move the overlay BrowserView to/from a popout Window.
void ToggleBrowserView();
// Update browser bounds.
void UpdateBounds(CefInsets insets);
// Exclude all regions obscured by overlays.
void UpdateDraggableRegions(std::vector<CefDraggableRegion>& window_regions);
bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id);
void PopOutWindowDestroyed();
private:
// CefViewDelegate methods:
CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
// CefBrowserViewDelegate methods:
void OnBrowserDestroyed(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowser> browser) override;
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) override;
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) override;
cef_runtime_style_t GetBrowserRuntimeStyle() override;
// Move the BrowserView to a new top-level Window.
void PopOutBrowserView();
// Return the BrowserView to the overlay.
void PopInBrowserView();
ViewsWindow* const owner_window_;
CefRefPtr<CefWindow> window_;
CefRefPtr<CefBrowserView> browser_view_;
CefRefPtr<CefOverlayController> controller_;
CefInsets last_insets_;
CefRefPtr<CefWindow> popout_window_;
base::WeakPtrFactory<ViewsOverlayBrowser> weak_ptr_factory_{this};
IMPLEMENT_REFCOUNTING(ViewsOverlayBrowser);
DISALLOW_COPY_AND_ASSIGN(ViewsOverlayBrowser);
};
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_

View File

@ -12,6 +12,7 @@
#include "include/cef_i18n_util.h" #include "include/cef_i18n_util.h"
#include "include/views/cef_box_layout.h" #include "include/views/cef_box_layout.h"
#include "include/wrapper/cef_helpers.h" #include "include/wrapper/cef_helpers.h"
#include "tests/cefclient/browser/default_client_handler.h"
#include "tests/cefclient/browser/main_context.h" #include "tests/cefclient/browser/main_context.h"
#include "tests/cefclient/browser/resource.h" #include "tests/cefclient/browser/resource.h"
#include "tests/cefclient/browser/views_style.h" #include "tests/cefclient/browser/views_style.h"
@ -146,6 +147,14 @@ CefRefPtr<ViewsWindow> ViewsWindow::Create(
CefRefPtr<ViewsWindow> views_window = CefRefPtr<ViewsWindow> views_window =
new ViewsWindow(type, delegate, nullptr, command_line); new ViewsWindow(type, delegate, nullptr, command_line);
// Only create an overlay browser for a primary window.
if (command_line->HasSwitch(switches::kShowOverlayBrowser)) {
views_window->with_overlay_browser_ = true;
views_window->initial_url_ = url;
views_window->settings_ = settings;
views_window->request_context_ = request_context;
}
const auto expected_browser_runtime_style = views_window->use_alloy_style_ const auto expected_browser_runtime_style = views_window->use_alloy_style_
? CEF_RUNTIME_STYLE_ALLOY ? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME; : CEF_RUNTIME_STYLE_CHROME;
@ -345,6 +354,8 @@ void ViewsWindow::SetDraggableRegions(
return; return;
} }
last_regions_ = regions;
// Convert the regions from BrowserView to Window coordinates. // Convert the regions from BrowserView to Window coordinates.
std::vector<CefDraggableRegion> window_regions = regions; std::vector<CefDraggableRegion> window_regions = regions;
for (auto& region : window_regions) { for (auto& region : window_regions) {
@ -359,6 +370,11 @@ void ViewsWindow::SetDraggableRegions(
overlay_controls_->UpdateDraggableRegions(window_regions); overlay_controls_->UpdateDraggableRegions(window_regions);
} }
if (overlay_browser_) {
// Exclude all regions obscured by overlays.
overlay_browser_->UpdateDraggableRegions(window_regions);
}
window_->SetDraggableRegions(window_regions); window_->SetDraggableRegions(window_regions);
} }
@ -434,6 +450,10 @@ void ViewsWindow::SetTitlebarHeight(const std::optional<float>& height) {
NudgeWindow(); NudgeWindow();
} }
void ViewsWindow::UpdateDraggableRegions() {
SetDraggableRegions(last_regions_);
}
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView( CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view, CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings, const CefBrowserSettings& settings,
@ -900,6 +920,8 @@ bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
if (command_id == ID_QUIT) { if (command_id == ID_QUIT) {
delegate_->OnExit(); delegate_->OnExit();
return true; return true;
} else if (overlay_browser_) {
return overlay_browser_->OnAccelerator(window, command_id);
} }
return false; return false;
@ -1001,6 +1023,19 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
CreateLocationBar(), CreateLocationBar(),
chrome_toolbar_type_ != CEF_CTT_NONE); chrome_toolbar_type_ != CEF_CTT_NONE);
} }
if (with_overlay_browser_) {
overlay_browser_ = new ViewsOverlayBrowser(this);
// Use default behavior for the overlay browser. A new |client| instance
// is still required by cefclient architecture.
CefRefPtr<CefClient> client =
new DefaultClientHandler(/*use_alloy_style=*/true);
overlay_browser_->Initialize(window_, client, initial_url_, settings_,
request_context_);
request_context_ = nullptr;
}
} else { } else {
// Remove any controls that may include the Chrome toolbar before removing // Remove any controls that may include the Chrome toolbar before removing
// the BrowserView. // the BrowserView.
@ -1015,6 +1050,10 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
location_bar_ = nullptr; location_bar_ = nullptr;
} }
} }
if (overlay_browser_) {
overlay_browser_->Destroy();
overlay_browser_ = nullptr;
}
} }
} }
@ -1028,6 +1067,12 @@ void ViewsWindow::OnLayoutChanged(CefRefPtr<CefView> view,
if (overlay_controls_) { if (overlay_controls_) {
overlay_controls_->UpdateControls(); overlay_controls_->UpdateControls();
} }
if (overlay_browser_) {
// TODO: Consider modifying insets based on toolbar visibility.
CefInsets window_insets(200, 200, 200, 200);
overlay_browser_->UpdateBounds(window_insets);
}
} }
void ViewsWindow::OnThemeChanged(CefRefPtr<CefView> view) { void ViewsWindow::OnThemeChanged(CefRefPtr<CefView> view) {
@ -1064,9 +1109,9 @@ ViewsWindow::ViewsWindow(WindowType type,
with_controls_ = is_normal_type && delegate_->WithControls(); with_controls_ = is_normal_type && delegate_->WithControls();
const bool hide_frame = command_line->HasSwitch(switches::kHideFrame); const bool hide_frame = command_line->HasSwitch(switches::kHideFrame);
const bool hide_overlays = const bool show_overlays = is_normal_type && hide_frame && !with_controls_ &&
!is_normal_type || command_line->HasSwitch(switches::kHideOverlays); !command_line->HasSwitch(switches::kHideOverlays);
const bool hide_toolbar = hide_overlays && !with_controls_; const bool hide_toolbar = !show_overlays && !with_controls_;
const bool show_window_buttons = const bool show_window_buttons =
command_line->HasSwitch(switches::kShowWindowButtons); command_line->HasSwitch(switches::kShowWindowButtons);
accepts_first_mouse_ = command_line->HasSwitch(switches::kAcceptsFirstMouse); accepts_first_mouse_ = command_line->HasSwitch(switches::kAcceptsFirstMouse);
@ -1075,7 +1120,7 @@ ViewsWindow::ViewsWindow(WindowType type,
frameless_ = hide_frame; frameless_ = hide_frame;
// With an overlay that mimics window controls. // With an overlay that mimics window controls.
with_overlay_controls_ = hide_frame && !hide_overlays && !with_controls_; with_overlay_controls_ = show_overlays;
// If window has frame or flag passed explicitly // If window has frame or flag passed explicitly
with_standard_buttons_ = !frameless_ || show_window_buttons; with_standard_buttons_ = !frameless_ || show_window_buttons;
@ -1313,12 +1358,12 @@ void ViewsWindow::SetMenuFocusable(bool focusable) {
if (menu_bar_) { if (menu_bar_) {
menu_bar_->SetMenuFocusable(focusable); menu_bar_->SetMenuFocusable(focusable);
} else { } else if (menu_button_) {
window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable); menu_button_->SetFocusable(focusable);
if (focusable) { if (focusable) {
// Give focus to menu button. // Give focus to menu button.
window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus(); menu_button_->RequestFocus();
} }
} }

View File

@ -27,6 +27,7 @@
#include "tests/cefclient/browser/image_cache.h" #include "tests/cefclient/browser/image_cache.h"
#include "tests/cefclient/browser/root_window.h" #include "tests/cefclient/browser/root_window.h"
#include "tests/cefclient/browser/views_menu_bar.h" #include "tests/cefclient/browser/views_menu_bar.h"
#include "tests/cefclient/browser/views_overlay_browser.h"
#include "tests/cefclient/browser/views_overlay_controls.h" #include "tests/cefclient/browser/views_overlay_controls.h"
namespace client { namespace client {
@ -128,6 +129,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
std::optional<CefRect>& dip_bounds); std::optional<CefRect>& dip_bounds);
void SetTitlebarHeight(const std::optional<float>& height); void SetTitlebarHeight(const std::optional<float>& height);
void UpdateDraggableRegions();
// CefBrowserViewDelegate methods: // CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView( CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view, CefRefPtr<CefBrowserView> browser_view,
@ -251,7 +254,7 @@ class ViewsWindow : public CefBrowserViewDelegate,
void NudgeWindow(); void NudgeWindow();
const WindowType type_; const WindowType type_;
Delegate* delegate_; // Not owned by this object. Delegate* const delegate_; // Not owned by this object.
const bool use_alloy_style_; const bool use_alloy_style_;
bool use_alloy_style_window_; bool use_alloy_style_window_;
CefRefPtr<CefBrowserView> browser_view_; CefRefPtr<CefBrowserView> browser_view_;
@ -280,6 +283,13 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefRefPtr<ViewsOverlayControls> overlay_controls_; CefRefPtr<ViewsOverlayControls> overlay_controls_;
// Overlay browser view state.
bool with_overlay_browser_ = false;
std::string initial_url_;
CefBrowserSettings settings_;
CefRefPtr<CefRequestContext> request_context_;
CefRefPtr<ViewsOverlayBrowser> overlay_browser_;
std::optional<float> default_titlebar_height_; std::optional<float> default_titlebar_height_;
std::optional<float> override_titlebar_height_; std::optional<float> override_titlebar_height_;
@ -293,6 +303,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
bool can_go_back_ = false; bool can_go_back_ = false;
bool can_go_forward_ = false; bool can_go_forward_ = false;
std::vector<CefDraggableRegion> last_regions_;
IMPLEMENT_REFCOUNTING(ViewsWindow); IMPLEMENT_REFCOUNTING(ViewsWindow);
DISALLOW_COPY_AND_ASSIGN(ViewsWindow); DISALLOW_COPY_AND_ASSIGN(ViewsWindow);
}; };

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);

View File

@ -522,68 +522,21 @@ void WindowIconFramelessImpl(CefRefPtr<CefWaitableEvent> event) {
TestWindowDelegate::RunTest(event, std::move(config)); TestWindowDelegate::RunTest(event, std::move(config));
} }
const int kChar = 'A'; constexpr int kChar = 'A';
const int kCloseWindowId = 2; constexpr int kCloseWindowId = 2;
bool got_accelerator; bool got_accelerator;
int got_key_event_alt_count;
bool got_key_event_char;
void TriggerAccelerator(CefRefPtr<CefWindow> window) { void TriggerAccelerator(CefRefPtr<CefWindow> window) {
window->SendKeyPress(kChar, EVENTFLAG_ALT_DOWN); window->SendKeyPress(kChar, EVENTFLAG_ALT_DOWN);
} }
bool OnKeyEvent(CefRefPtr<CefWindow> window, const CefKeyEvent& event) {
if (event.type != KEYEVENT_RAWKEYDOWN) {
return false;
}
if (event.windows_key_code == VK_MENU) {
// First we get the ALT key press in all cases.
EXPECT_FALSE(got_key_event_char);
if (got_key_event_alt_count == 0) {
EXPECT_FALSE(got_accelerator);
} else {
EXPECT_TRUE(got_accelerator);
}
EXPECT_EQ(EVENTFLAG_ALT_DOWN, static_cast<int>(event.modifiers));
got_key_event_alt_count++;
} else if (event.windows_key_code == kChar) {
// Then we get the char key press with the ALT modifier if the accelerator
// isn't registered.
EXPECT_TRUE(got_accelerator);
EXPECT_EQ(got_key_event_alt_count, 2);
EXPECT_FALSE(got_key_event_char);
EXPECT_EQ(EVENTFLAG_ALT_DOWN, static_cast<int>(event.modifiers));
got_key_event_char = true;
// Call this method just to make sure it doesn't crash.
window->RemoveAllAccelerators();
// End the test by closing the Window.
window->Close();
return true;
}
return false;
}
bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) { bool OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
EXPECT_FALSE(got_accelerator); EXPECT_FALSE(got_accelerator);
EXPECT_EQ(got_key_event_alt_count, 1);
EXPECT_FALSE(got_key_event_char);
EXPECT_EQ(kCloseWindowId, command_id); EXPECT_EQ(kCloseWindowId, command_id);
got_accelerator = true; got_accelerator = true;
// Remove the accelerator. window->Close();
window->RemoveAccelerator(kCloseWindowId);
// Now send the event without the accelerator registered. Should result in a
// call to OnKeyEvent.
TriggerAccelerator(window);
return true; return true;
} }
@ -598,25 +551,15 @@ void RunWindowAccelerator(CefRefPtr<CefWindow> window) {
void VerifyWindowAccelerator(CefRefPtr<CefWindow> window) { void VerifyWindowAccelerator(CefRefPtr<CefWindow> window) {
EXPECT_TRUE(got_accelerator); EXPECT_TRUE(got_accelerator);
EXPECT_EQ(got_key_event_alt_count, 2);
EXPECT_TRUE(got_key_event_char);
} }
// Expected order of events:
// 1. OnKeyEvent for ALT key press.
// 2. OnAccelerator for ALT+Char key press (with accelerator registered).
// 3. OnKeyEvent for ALT key press.
// 4. OnKeyEvent for ALT+Char key press (without accelerator registered).
void WindowAcceleratorImpl(CefRefPtr<CefWaitableEvent> event) { void WindowAcceleratorImpl(CefRefPtr<CefWaitableEvent> event) {
got_accelerator = false; got_accelerator = false;
got_key_event_alt_count = 0;
got_key_event_char = false;
auto config = std::make_unique<TestWindowDelegate::Config>(); auto config = std::make_unique<TestWindowDelegate::Config>();
config->on_window_created = base::BindOnce(RunWindowAccelerator); config->on_window_created = base::BindOnce(RunWindowAccelerator);
config->on_window_destroyed = base::BindOnce(VerifyWindowAccelerator); config->on_window_destroyed = base::BindOnce(VerifyWindowAccelerator);
config->on_accelerator = base::BindRepeating(OnAccelerator); config->on_accelerator = base::BindRepeating(OnAccelerator);
config->on_key_event = base::BindRepeating(OnKeyEvent);
config->close_window = false; config->close_window = false;
TestWindowDelegate::RunTest(event, std::move(config)); TestWindowDelegate::RunTest(event, std::move(config));
} }

View File

@ -58,5 +58,6 @@ const char kHideWindowOnClose[] = "hide-window-on-close";
const char kAcceptsFirstMouse[] = "accepts-first-mouse"; const char kAcceptsFirstMouse[] = "accepts-first-mouse";
const char kUseAlloyStyle[] = "use-alloy-style"; const char kUseAlloyStyle[] = "use-alloy-style";
const char kUseChromeStyleWindow[] = "use-chrome-style-window"; const char kUseChromeStyleWindow[] = "use-chrome-style-window";
const char kShowOverlayBrowser[] = "show-overlay-browser";
} // namespace client::switches } // namespace client::switches

View File

@ -52,6 +52,7 @@ extern const char kHideWindowOnClose[];
extern const char kAcceptsFirstMouse[]; extern const char kAcceptsFirstMouse[];
extern const char kUseAlloyStyle[]; extern const char kUseAlloyStyle[];
extern const char kUseChromeStyleWindow[]; extern const char kUseChromeStyleWindow[];
extern const char kShowOverlayBrowser[];
} // namespace client::switches } // namespace client::switches