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

View File

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

View File

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

View File

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

View File

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

View File

@ -452,6 +452,20 @@ bool CefBrowserHostBase::HasView() {
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) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,

View File

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

View File

@ -63,6 +63,10 @@ class PopupWindowDelegate : public CefWindowDelegate {
return true;
}
cef_runtime_style_t GetWindowRuntimeStyle() override {
return browser_view_->GetRuntimeStyle();
}
private:
CefRefPtr<CefBrowserView> browser_view_;
@ -195,8 +199,9 @@ void CefBrowserPlatformDelegate::PopupWebContentsCreated(
}
CefRefPtr<CefBrowserViewDelegate> new_delegate;
CefRefPtr<CefBrowserViewDelegate> opener_delegate;
cef_runtime_style_t opener_runtime_style = CEF_RUNTIME_STYLE_DEFAULT;
auto browser_view = GetBrowserView();
if (browser_view) {
// 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);
}
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.
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.
new_platform_delegate->SetBrowserView(new_browser_view);
// Keep the BrowserView alive until PopupBrowserCreated() is called.
new_browser_view->AddRef();
}
void CefBrowserPlatformDelegate::PopupBrowserCreated(
@ -255,6 +270,9 @@ void CefBrowserPlatformDelegate::PopupBrowserCreated(
CefWindow::CreateTopLevelWindow(
new PopupWindowDelegate(new_browser_view.get()));
}
// Release the reference added in PopupWebContentsCreated().
new_browser_view->Release();
}
CefRefPtr<CefBrowserViewDelegate>

View File

@ -165,14 +165,47 @@ void ChromeBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
}
void ChromeBrowserHostImpl::CloseBrowser(bool force_close) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::CloseBrowser,
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, force_close));
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&ChromeBrowserHostImpl::DoCloseBrowser, this));
}
bool ChromeBrowserHostImpl::TryCloseBrowser() {
// TODO(chrome): Handle the case where the browser may not close immediately.
CloseBrowser(true);
if (!CEF_CURRENTLY_ON_UIT()) {
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;
}
@ -551,7 +584,7 @@ void ChromeBrowserHostImpl::DestroyBrowser() {
// WebContents first. See comments on CefBrowserHostBase::DestroyBrowser.
if (GetWebContents()) {
// Triggers a call to OnWebContentsDestroyed.
DoCloseBrowser(/*force_close=*/true);
DoCloseBrowser();
DCHECK(!GetWebContents());
}
@ -565,15 +598,13 @@ void ChromeBrowserHostImpl::DestroyBrowser() {
CefBrowserHostBase::DestroyBrowser();
}
void ChromeBrowserHostImpl::DoCloseBrowser(bool force_close) {
void ChromeBrowserHostImpl::DoCloseBrowser() {
CEF_REQUIRE_UIT();
if (browser_) {
// Like chrome::CloseTab() but specifying the WebContents.
const int tab_index = GetCurrentTabIndex();
if (tab_index != TabStripModel::kNoTab) {
// 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(
tab_index, TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB |
TabCloseTypes::CLOSE_USER_GESTURE);

View File

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

View File

@ -17,6 +17,7 @@
#if BUILDFLAG(IS_LINUX)
#include "base/linux_util.h"
#include "cef/libcef/browser/printing/print_dialog_linux.h"
#endif
#if BUILDFLAG(IS_WIN)
@ -83,4 +84,11 @@ void ChromeBrowserMainExtraPartsCef::ToolkitInitialized() {
// Override the default Chrome client.
SetConstrainedWindowViewsClient(CreateAlloyConstrainedWindowViewsClient(
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) {
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(
@ -37,7 +38,10 @@ void CefBrowserPlatformDelegateChromeViews::WebContentsCreated(
void CefBrowserPlatformDelegateChromeViews::WebContentsDestroyed(
content::WebContents* web_contents) {
CefBrowserPlatformDelegateChrome::WebContentsDestroyed(web_contents);
browser_view_->WebContentsDestroyed(web_contents);
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->WebContentsDestroyed(web_contents);
}
}
void CefBrowserPlatformDelegateChromeViews::BrowserCreated(
@ -48,7 +52,7 @@ void CefBrowserPlatformDelegateChromeViews::BrowserCreated(
void CefBrowserPlatformDelegateChromeViews::NotifyBrowserCreated() {
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
// don't currently have the ability to intercept WebContents creation via
@ -75,8 +79,9 @@ void CefBrowserPlatformDelegateChromeViews::NotifyBrowserCreated() {
}
void CefBrowserPlatformDelegateChromeViews::NotifyBrowserDestroyed() {
if (browser_view_->delegate()) {
browser_view_->delegate()->OnBrowserDestroyed(browser_view_,
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_ && browser_view_->delegate()) {
browser_view_->delegate()->OnBrowserDestroyed(browser_view_.get(),
browser_.get());
}
}
@ -84,7 +89,11 @@ void CefBrowserPlatformDelegateChromeViews::NotifyBrowserDestroyed() {
void CefBrowserPlatformDelegateChromeViews::BrowserDestroyed(
CefBrowserHostBase* browser) {
CefBrowserPlatformDelegateChrome::BrowserDestroyed(browser);
browser_view_->BrowserDestroyed(browser);
// |browser_view_| may be destroyed before this callback arrives.
if (browser_view_) {
browser_view_->BrowserDestroyed(browser);
}
browser_view_ = nullptr;
}
void CefBrowserPlatformDelegateChromeViews::CloseHostWindow() {
@ -100,7 +109,7 @@ CefWindowHandle CefBrowserPlatformDelegateChromeViews::GetHostWindowHandle()
}
views::Widget* CefBrowserPlatformDelegateChromeViews::GetWindowWidget() const {
if (browser_view_->root_view()) {
if (browser_view_ && browser_view_->root_view()) {
return browser_view_->root_view()->GetWidget();
}
return nullptr;

View File

@ -5,6 +5,7 @@
#ifndef 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/views/browser_view_impl.h"
@ -33,12 +34,17 @@ class CefBrowserPlatformDelegateChromeViews
void SetBrowserView(CefRefPtr<CefBrowserView> browser_view) override;
bool IsViewsHosted() const override;
CefRefPtr<CefBrowserViewImpl> browser_view() const { return browser_view_; }
CefBrowserViewImpl* browser_view() const { return browser_view_.get(); }
private:
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_

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@
#ifndef 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/native/browser_platform_delegate_native.h"
#include "cef/libcef/browser/views/browser_view_impl.h"
@ -65,7 +66,13 @@ class CefBrowserPlatformDelegateViews
private:
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_

View File

@ -35,30 +35,39 @@ std::optional<cef_gesture_command_t> GetGestureCommand(
return std::nullopt;
}
bool ComputeAlloyStyle(CefBrowserViewDelegate* cef_delegate,
bool is_devtools_popup) {
// Alloy style is not supported with Chrome DevTools popups.
const bool supports_alloy_style = !is_devtools_popup;
const auto default_style = CEF_RUNTIME_STYLE_CHROME;
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 "
"Chrome style is supported";
}
} else if (requested_style == CEF_RUNTIME_STYLE_CHROME) {
// Chrome style is always supported.
result_style = requested_style;
bool ComputeAlloyStyle(
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.
if (cef_delegate &&
cef_delegate->GetBrowserRuntimeStyle() == CEF_RUNTIME_STYLE_ALLOY) {
LOG(ERROR) << "GetBrowserRuntimeStyle() requested Alloy style; only "
"Chrome style is supported for DevTools popups";
}
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
@ -111,7 +120,8 @@ CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::Create(
}
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(
window_info, client, url, settings, extra_info, request_context);
browser_view->Initialize();
@ -123,16 +133,38 @@ CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::Create(
CefRefPtr<CefBrowserViewImpl> CefBrowserViewImpl::CreateForPopup(
const CefBrowserSettings& settings,
CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools) {
bool is_devtools,
cef_runtime_style_t opener_runtime_style) {
CEF_REQUIRE_UIT_RETURN(nullptr);
CefRefPtr<CefBrowserViewImpl> browser_view =
new CefBrowserViewImpl(delegate, is_devtools);
new CefBrowserViewImpl(delegate, is_devtools, opener_runtime_style);
browser_view->Initialize();
browser_view->SetDefaults(settings);
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(
content::WebContents* web_contents) {
if (web_view()) {
@ -269,8 +301,8 @@ void CefBrowserViewImpl::AddedToWidget() {
CefWidget* cef_widget = CefWidget::GetForWidget(widget);
DCHECK(cef_widget);
if (!browser_) {
if (cef_widget->IsAlloyStyle() && !is_alloy_style_) {
if (!browser_ && !is_alloy_style_) {
if (cef_widget->IsAlloyStyle()) {
LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window";
return;
}
@ -337,9 +369,12 @@ bool CefBrowserViewImpl::OnGestureEvent(ui::GestureEvent* event) {
CefBrowserViewImpl::CefBrowserViewImpl(
CefRefPtr<CefBrowserViewDelegate> delegate,
bool is_devtools_popup)
bool is_devtools_popup,
std::optional<cef_runtime_style_t> opener_runtime_style)
: 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) {}
void CefBrowserViewImpl::SetPendingBrowserCreateParams(

View File

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

View File

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

View File

@ -138,7 +138,12 @@ void CefNativeWidgetMac::OnWindowFullscreenTransitionComplete() {
}
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;
}
@ -146,10 +151,12 @@ void CefNativeWidgetMac::OnWindowInitialized() {
if (auto* bridge = GetInProcessNSWindowBridge()) {
bridge->SetCommandDispatcher([[ChromeCommandDispatcherDelegate alloc] init],
[[BrowserWindowCommandHandler alloc] init]);
} else {
initialized_ = true;
} else if (browser_view_) {
if (auto* host = GetHostForBrowser(browser_view_->browser())) {
host->GetAppShim()->CreateCommandDispatcherForWidget(
GetNSWindowHost()->bridged_native_widget_id());
initialized_ = true;
}
}
}

View File

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

View File

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

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=434a753c90262b051077f7a79f3106ac52ffbf75$
// $hash=8a2a8a4853c3869876ffad3e6c175945ac1c5021$
//
#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;
}
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,
int focus) {
shutdown_checker::AssertNotShutdown();
@ -1514,6 +1532,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
GetStruct()->get_browser = browser_host_get_browser;
GetStruct()->close_browser = browser_host_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()->get_window_handle = browser_host_get_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
// for more information.
//
// $hash=dac19ba091b3acf3e1587b176e28bc9f9c8c8dd0$
// $hash=2319d794dd3a38c448908114d1b4ea37b34f89dd$
//
#include "libcef_dll/ctocpp/browser_host_ctocpp.h"
@ -131,6 +131,23 @@ NO_SANITIZE("cfi-icall") bool CefBrowserHostCToCpp::TryCloseBrowser() {
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) {
shutdown_checker::AssertNotShutdown();
@ -1111,8 +1128,8 @@ void CefBrowserHostCToCpp::DragSourceSystemDragEnded() {
}
NO_SANITIZE("cfi-icall")
CefRefPtr<
CefNavigationEntry> CefBrowserHostCToCpp::GetVisibleNavigationEntry() {
CefRefPtr<CefNavigationEntry>
CefBrowserHostCToCpp::GetVisibleNavigationEntry() {
shutdown_checker::AssertNotShutdown();
cef_browser_host_t* _struct = GetStruct();

View File

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

View File

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

View File

@ -4,8 +4,16 @@
#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 {
DefaultClientHandler::DefaultClientHandler(std::optional<bool> use_alloy_style)
: use_alloy_style_(
use_alloy_style.value_or(MainContext::Get()->UseAlloyStyleGlobal())) {
}
// static
CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
CefRefPtr<CefClient> client) {
@ -16,4 +24,39 @@ CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
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

View File

@ -6,6 +6,8 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_
#pragma once
#include <optional>
#include "tests/cefclient/browser/base_client_handler.h"
namespace client {
@ -14,18 +16,37 @@ namespace client {
// style only.
class DefaultClientHandler : public BaseClientHandler {
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
// not a DefaultClientHandler.
static CefRefPtr<DefaultClientHandler> GetForClient(
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:
// Used to determine the object type.
virtual const void* GetTypeKey() const override { return &kTypeKey; }
static constexpr int kTypeKey = 0;
const bool use_alloy_style_;
IMPLEMENT_REFCOUNTING(DefaultClientHandler);
DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler);
};

View File

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

View File

@ -134,8 +134,11 @@ scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) {
// Use default window creation for the popup. A new |client| instance is
// still required by cefclient architecture.
client = new DefaultClientHandler();
// required by cefclient architecture if the type is not already
// DefaultClientHandler.
if (!DefaultClientHandler::GetForClient(client)) {
client = new DefaultClientHandler(use_alloy_style);
}
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/views/cef_box_layout.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/resource.h"
#include "tests/cefclient/browser/views_style.h"
@ -146,6 +147,14 @@ CefRefPtr<ViewsWindow> ViewsWindow::Create(
CefRefPtr<ViewsWindow> views_window =
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_
? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME;
@ -345,6 +354,8 @@ void ViewsWindow::SetDraggableRegions(
return;
}
last_regions_ = regions;
// Convert the regions from BrowserView to Window coordinates.
std::vector<CefDraggableRegion> window_regions = regions;
for (auto& region : window_regions) {
@ -359,6 +370,11 @@ void ViewsWindow::SetDraggableRegions(
overlay_controls_->UpdateDraggableRegions(window_regions);
}
if (overlay_browser_) {
// Exclude all regions obscured by overlays.
overlay_browser_->UpdateDraggableRegions(window_regions);
}
window_->SetDraggableRegions(window_regions);
}
@ -434,6 +450,10 @@ void ViewsWindow::SetTitlebarHeight(const std::optional<float>& height) {
NudgeWindow();
}
void ViewsWindow::UpdateDraggableRegions() {
SetDraggableRegions(last_regions_);
}
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
@ -900,6 +920,8 @@ bool ViewsWindow::OnAccelerator(CefRefPtr<CefWindow> window, int command_id) {
if (command_id == ID_QUIT) {
delegate_->OnExit();
return true;
} else if (overlay_browser_) {
return overlay_browser_->OnAccelerator(window, command_id);
}
return false;
@ -1001,6 +1023,19 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
CreateLocationBar(),
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 {
// Remove any controls that may include the Chrome toolbar before removing
// the BrowserView.
@ -1015,6 +1050,10 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
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_) {
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) {
@ -1064,9 +1109,9 @@ ViewsWindow::ViewsWindow(WindowType type,
with_controls_ = is_normal_type && delegate_->WithControls();
const bool hide_frame = command_line->HasSwitch(switches::kHideFrame);
const bool hide_overlays =
!is_normal_type || command_line->HasSwitch(switches::kHideOverlays);
const bool hide_toolbar = hide_overlays && !with_controls_;
const bool show_overlays = is_normal_type && hide_frame && !with_controls_ &&
!command_line->HasSwitch(switches::kHideOverlays);
const bool hide_toolbar = !show_overlays && !with_controls_;
const bool show_window_buttons =
command_line->HasSwitch(switches::kShowWindowButtons);
accepts_first_mouse_ = command_line->HasSwitch(switches::kAcceptsFirstMouse);
@ -1075,7 +1120,7 @@ ViewsWindow::ViewsWindow(WindowType type,
frameless_ = hide_frame;
// 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
with_standard_buttons_ = !frameless_ || show_window_buttons;
@ -1313,12 +1358,12 @@ void ViewsWindow::SetMenuFocusable(bool focusable) {
if (menu_bar_) {
menu_bar_->SetMenuFocusable(focusable);
} else {
window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable);
} else if (menu_button_) {
menu_button_->SetFocusable(focusable);
if (focusable) {
// 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/root_window.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"
namespace client {
@ -128,6 +129,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
std::optional<CefRect>& dip_bounds);
void SetTitlebarHeight(const std::optional<float>& height);
void UpdateDraggableRegions();
// CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
@ -251,7 +254,7 @@ class ViewsWindow : public CefBrowserViewDelegate,
void NudgeWindow();
const WindowType type_;
Delegate* delegate_; // Not owned by this object.
Delegate* const delegate_; // Not owned by this object.
const bool use_alloy_style_;
bool use_alloy_style_window_;
CefRefPtr<CefBrowserView> browser_view_;
@ -280,6 +283,13 @@ class ViewsWindow : public CefBrowserViewDelegate,
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> override_titlebar_height_;
@ -293,6 +303,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
bool can_go_back_ = false;
bool can_go_forward_ = false;
std::vector<CefDraggableRegion> last_regions_;
IMPLEMENT_REFCOUNTING(ViewsWindow);
DISALLOW_COPY_AND_ASSIGN(ViewsWindow);
};

View File

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

View File

@ -322,6 +322,8 @@ void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
EXPECT_UI_THREAD();
EXPECT_TRUE(browser->GetHost()->IsReadyToBeClosed());
// Free the browser pointer so that the browser can be destroyed.
const int browser_id = browser->GetIdentifier();
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));
}
const int kChar = 'A';
const int kCloseWindowId = 2;
constexpr int kChar = 'A';
constexpr int kCloseWindowId = 2;
bool got_accelerator;
int got_key_event_alt_count;
bool got_key_event_char;
void TriggerAccelerator(CefRefPtr<CefWindow> window) {
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) {
EXPECT_FALSE(got_accelerator);
EXPECT_EQ(got_key_event_alt_count, 1);
EXPECT_FALSE(got_key_event_char);
EXPECT_EQ(kCloseWindowId, command_id);
got_accelerator = true;
// Remove the accelerator.
window->RemoveAccelerator(kCloseWindowId);
// Now send the event without the accelerator registered. Should result in a
// call to OnKeyEvent.
TriggerAccelerator(window);
window->Close();
return true;
}
@ -598,25 +551,15 @@ void RunWindowAccelerator(CefRefPtr<CefWindow> window) {
void VerifyWindowAccelerator(CefRefPtr<CefWindow> window) {
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) {
got_accelerator = false;
got_key_event_alt_count = 0;
got_key_event_char = false;
auto config = std::make_unique<TestWindowDelegate::Config>();
config->on_window_created = base::BindOnce(RunWindowAccelerator);
config->on_window_destroyed = base::BindOnce(VerifyWindowAccelerator);
config->on_accelerator = base::BindRepeating(OnAccelerator);
config->on_key_event = base::BindRepeating(OnKeyEvent);
config->close_window = false;
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 kUseAlloyStyle[] = "use-alloy-style";
const char kUseChromeStyleWindow[] = "use-chrome-style-window";
const char kShowOverlayBrowser[] = "show-overlay-browser";
} // namespace client::switches

View File

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