cefclient: views: Support window state restore (see issue #3359)

The cefclient sample app will persist window state across application restart
if run with views, cache_path and persist_user_references enabled.

To test:
1. Run `cefclient --use-views --cache-path=/path/to/cache --persist-user-preferences`
2. Move or resize the window, maximize, minimize, etc.
3. Exit cefclient.
4. Run cefclient again with the same arguments. The previous window state will
   be restored.
This commit is contained in:
Marshall Greenblatt 2022-10-28 14:17:18 -04:00
parent 09bb643ef6
commit 882bc19fdd
18 changed files with 511 additions and 65 deletions

View File

@ -234,6 +234,8 @@
'tests/cefclient/browser/client_handler_osr.h',
'tests/cefclient/browser/client_handler_std.cc',
'tests/cefclient/browser/client_handler_std.h',
'tests/cefclient/browser/client_prefs.cc',
'tests/cefclient/browser/client_prefs.h',
'tests/cefclient/browser/client_types.h',
'tests/cefclient/browser/dialog_test.cc',
'tests/cefclient/browser/dialog_test.h',

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=ea84b76b6965d1419e416581d87e82f74680bd07$
// $hash=e1657ed68132b846ad638dc87bc5ee9b9c10f014$
//
#ifndef CEF_INCLUDE_CAPI_VIEWS_CEF_WINDOW_DELEGATE_CAPI_H_
@ -65,6 +65,12 @@ typedef struct _cef_window_delegate_t {
void(CEF_CALLBACK* on_window_created)(struct _cef_window_delegate_t* self,
struct _cef_window_t* window);
///
/// Called when |window| is closing.
///
void(CEF_CALLBACK* on_window_closing)(struct _cef_window_delegate_t* self,
struct _cef_window_t* window);
///
/// Called when |window| is destroyed. Release all references to |window| and
/// do not attempt to execute any functions on |window| after this callback
@ -81,6 +87,15 @@ typedef struct _cef_window_delegate_t {
struct _cef_window_t* window,
int active);
///
/// Called when |window| bounds have changed. |new_bounds| will be in DIP
/// screen coordinates.
///
void(CEF_CALLBACK* on_window_bounds_changed)(
struct _cef_window_delegate_t* self,
struct _cef_window_t* window,
const cef_rect_t* new_bounds);
///
/// Return the parent for |window| or NULL if the |window| does not have a
/// parent. Windows with parents will not get a taskbar button. Set |is_menu|

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 "043531d536c019ddd0c7c3096e7642bda2748755"
#define CEF_API_HASH_UNIVERSAL "46cd296ee58a28371e4f82c52cf447c516a4dc4b"
#if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "4f82dbb840cb3ce49078c43de0c9d96b4aec37b8"
#define CEF_API_HASH_PLATFORM "92c7ff6ca220e1c55081d020fd2b1d96d102a8fa"
#elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "a6da25ff35d7fb833a3f91bc4586a21354c90457"
#define CEF_API_HASH_PLATFORM "7d01ca5ec82dbefa6f3f84fa4883a0059d96ccb5"
#elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "72eeaa876bc8c0a6f2fba5c54c855375683f2b9e"
#define CEF_API_HASH_PLATFORM "f0334f17e0823c7cf7ee15bb53960efb21d31468"
#endif
#ifdef __cplusplus

View File

@ -55,6 +55,12 @@ class CefWindowDelegate : public CefPanelDelegate {
/*--cef()--*/
virtual void OnWindowCreated(CefRefPtr<CefWindow> window) {}
///
/// Called when |window| is closing.
///
/*--cef()--*/
virtual void OnWindowClosing(CefRefPtr<CefWindow> window) {}
///
/// Called when |window| is destroyed. Release all references to |window| and
/// do not attempt to execute any methods on |window| after this callback
@ -70,6 +76,14 @@ class CefWindowDelegate : public CefPanelDelegate {
virtual void OnWindowActivationChanged(CefRefPtr<CefWindow> window,
bool active) {}
///
/// Called when |window| bounds have changed. |new_bounds| will be in DIP
/// screen coordinates.
///
/*--cef()--*/
virtual void OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
const CefRect& new_bounds) {}
///
/// Return the parent for |window| or NULL if the |window| does not have a
/// parent. Windows with parents will not get a taskbar button. Set |is_menu|

View File

@ -391,6 +391,9 @@ void CefWindowImpl::OnWindowClosing() {
#if defined(USE_AURA)
unhandled_key_event_handler_.reset();
#endif
if (delegate())
delegate()->OnWindowClosing(this);
}
void CefWindowImpl::OnWindowViewDeleted() {

View File

@ -535,6 +535,12 @@ void CefWindowView::OnWidgetActivationChanged(views::Widget* widget,
void CefWindowView::OnWidgetBoundsChanged(views::Widget* widget,
const gfx::Rect& new_bounds) {
MoveOverlaysIfNecessary();
if (cef_delegate()) {
cef_delegate()->OnWindowBoundsChanged(
GetCefWindow(), {new_bounds.x(), new_bounds.y(), new_bounds.width(),
new_bounds.height()});
}
}
display::Display CefWindowView::GetDisplay() const {

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=2ecdf3e890e54e962286430f350c5b49249a9a9e$
// $hash=9657432e6ca2ba72aeeb1ced5c8cf5ee71cf7221$
//
#include "libcef_dll/cpptoc/views/window_delegate_cpptoc.h"
@ -41,6 +41,26 @@ window_delegate_on_window_created(struct _cef_window_delegate_t* self,
CefWindowCToCpp::Wrap(window));
}
void CEF_CALLBACK
window_delegate_on_window_closing(struct _cef_window_delegate_t* self,
cef_window_t* window) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: window; type: refptr_diff
DCHECK(window);
if (!window)
return;
// Execute
CefWindowDelegateCppToC::Get(self)->OnWindowClosing(
CefWindowCToCpp::Wrap(window));
}
void CEF_CALLBACK
window_delegate_on_window_destroyed(struct _cef_window_delegate_t* self,
cef_window_t* window) {
@ -82,6 +102,34 @@ void CEF_CALLBACK window_delegate_on_window_activation_changed(
CefWindowCToCpp::Wrap(window), active ? true : false);
}
void CEF_CALLBACK
window_delegate_on_window_bounds_changed(struct _cef_window_delegate_t* self,
cef_window_t* window,
const cef_rect_t* new_bounds) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: window; type: refptr_diff
DCHECK(window);
if (!window)
return;
// Verify param: new_bounds; type: simple_byref_const
DCHECK(new_bounds);
if (!new_bounds)
return;
// Translate param: new_bounds; type: simple_byref_const
CefRect new_boundsVal = new_bounds ? *new_bounds : CefRect();
// Execute
CefWindowDelegateCppToC::Get(self)->OnWindowBoundsChanged(
CefWindowCToCpp::Wrap(window), new_boundsVal);
}
cef_window_t* CEF_CALLBACK
window_delegate_get_parent_window(struct _cef_window_delegate_t* self,
cef_window_t* window,
@ -588,9 +636,12 @@ void CEF_CALLBACK window_delegate_on_blur(struct _cef_view_delegate_t* self,
CefWindowDelegateCppToC::CefWindowDelegateCppToC() {
GetStruct()->on_window_created = window_delegate_on_window_created;
GetStruct()->on_window_closing = window_delegate_on_window_closing;
GetStruct()->on_window_destroyed = window_delegate_on_window_destroyed;
GetStruct()->on_window_activation_changed =
window_delegate_on_window_activation_changed;
GetStruct()->on_window_bounds_changed =
window_delegate_on_window_bounds_changed;
GetStruct()->get_parent_window = window_delegate_get_parent_window;
GetStruct()->get_initial_bounds = window_delegate_get_initial_bounds;
GetStruct()->get_initial_show_state = window_delegate_get_initial_show_state;

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=423d12cda6148d99e5afb0cc4aa11818e0740e1e$
// $hash=07d3613588fb17a2d07d817a4b1390db3b6cffe7$
//
#include "libcef_dll/ctocpp/views/window_delegate_ctocpp.h"
@ -38,6 +38,25 @@ void CefWindowDelegateCToCpp::OnWindowCreated(CefRefPtr<CefWindow> window) {
_struct->on_window_created(_struct, CefWindowCppToC::Wrap(window));
}
NO_SANITIZE("cfi-icall")
void CefWindowDelegateCToCpp::OnWindowClosing(CefRefPtr<CefWindow> window) {
shutdown_checker::AssertNotShutdown();
cef_window_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_window_closing))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: window; type: refptr_diff
DCHECK(window.get());
if (!window.get())
return;
// Execute
_struct->on_window_closing(_struct, CefWindowCppToC::Wrap(window));
}
NO_SANITIZE("cfi-icall")
void CefWindowDelegateCToCpp::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
shutdown_checker::AssertNotShutdown();
@ -79,6 +98,27 @@ void CefWindowDelegateCToCpp::OnWindowActivationChanged(
active);
}
NO_SANITIZE("cfi-icall")
void CefWindowDelegateCToCpp::OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
const CefRect& new_bounds) {
shutdown_checker::AssertNotShutdown();
cef_window_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_window_bounds_changed))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: window; type: refptr_diff
DCHECK(window.get());
if (!window.get())
return;
// Execute
_struct->on_window_bounds_changed(_struct, CefWindowCppToC::Wrap(window),
&new_bounds);
}
NO_SANITIZE("cfi-icall")
CefRefPtr<CefWindow> CefWindowDelegateCToCpp::GetParentWindow(
CefRefPtr<CefWindow> window,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=4041b496f1e9ed673f99211be26b8fa98967fece$
// $hash=a7d0db45a4032026a7235d653b3cfed9929ad519$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_VIEWS_WINDOW_DELEGATE_CTOCPP_H_
@ -38,9 +38,12 @@ class CefWindowDelegateCToCpp
// CefWindowDelegate methods.
void OnWindowCreated(CefRefPtr<CefWindow> window) override;
void OnWindowClosing(CefRefPtr<CefWindow> window) override;
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override;
void OnWindowActivationChanged(CefRefPtr<CefWindow> window,
bool active) override;
void OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
const CefRect& new_bounds) override;
CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
bool* is_menu,
bool* can_activate_menu) override;

View File

@ -8,6 +8,7 @@
#include "include/cef_command_line.h"
#include "include/cef_crash_util.h"
#include "include/cef_file_util.h"
#include "tests/cefclient/browser/client_prefs.h"
#include "tests/shared/common/client_switches.h"
namespace client {
@ -19,6 +20,16 @@ class ClientBrowserDelegate : public ClientAppBrowser::Delegate {
public:
ClientBrowserDelegate() {}
void OnRegisterCustomPreferences(
CefRefPtr<client::ClientAppBrowser> app,
cef_preferences_type_t type,
CefRawPtr<CefPreferenceRegistrar> registrar) override {
if (type == CEF_PREFERENCES_TYPE_GLOBAL) {
// Register global preferences with default values.
prefs::RegisterGlobalPreferences(registrar);
}
}
void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) override {
if (CefCrashReportingEnabled()) {
// Set some crash keys for testing purposes. Keys must be defined in the

View File

@ -0,0 +1,202 @@
// Copyright (c) 2022 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/client_prefs.h"
#include <memory>
#include "include/base/cef_logging.h"
#include "include/cef_command_line.h"
#include "include/cef_preference.h"
#include "include/cef_values.h"
#include "include/views/cef_display.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/shared/common/client_switches.h"
#include "tests/shared/common/string_util.h"
namespace client {
namespace prefs {
namespace {
constexpr char kPrefWindowRestore[] = "cefclient.window_restore";
constexpr char kWindowRestoreStateKey[] = "state";
constexpr char kWindowRestoreBoundsKey[] = "bounds";
constexpr char kWindowRestoreBoundsKey_X[] = "x";
constexpr char kWindowRestoreBoundsKey_Y[] = "y";
constexpr char kWindowRestoreBoundsKey_W[] = "w";
constexpr char kWindowRestoreBoundsKey_H[] = "h";
static struct {
const char* str;
cef_show_state_t state;
} const kWindowRestoreStateValueMap[] = {
{"normal", CEF_SHOW_STATE_NORMAL},
{"minimized", CEF_SHOW_STATE_MINIMIZED},
{"maximized", CEF_SHOW_STATE_MAXIMIZED},
{"fullscreen", CEF_SHOW_STATE_FULLSCREEN},
};
std::optional<cef_show_state_t> ShowStateFromString(const std::string& str) {
const auto strLower = AsciiStrToLower(str);
for (size_t i = 0; i < std::size(kWindowRestoreStateValueMap); ++i) {
if (strLower == kWindowRestoreStateValueMap[i].str) {
return kWindowRestoreStateValueMap[i].state;
}
}
return std::nullopt;
}
const char* ShowStateToString(cef_show_state_t show_state) {
for (size_t i = 0; i < std::size(kWindowRestoreStateValueMap); ++i) {
if (show_state == kWindowRestoreStateValueMap[i].state) {
return kWindowRestoreStateValueMap[i].str;
}
}
NOTREACHED();
return nullptr;
}
// Create the CefValue representation that will be stored in preferences.
CefRefPtr<CefValue> CreateWindowRestoreValue(
cef_show_state_t show_state,
std::optional<CefRect> dip_bounds) {
auto dict = CefDictionaryValue::Create();
// Show state is required.
dict->SetString(kWindowRestoreStateKey, ShowStateToString(show_state));
// Bounds is optional.
if (dip_bounds) {
auto bounds_dict = CefDictionaryValue::Create();
bounds_dict->SetInt(kWindowRestoreBoundsKey_X, dip_bounds->x);
bounds_dict->SetInt(kWindowRestoreBoundsKey_Y, dip_bounds->y);
bounds_dict->SetInt(kWindowRestoreBoundsKey_W, dip_bounds->width);
bounds_dict->SetInt(kWindowRestoreBoundsKey_H, dip_bounds->height);
dict->SetDictionary(kWindowRestoreBoundsKey, bounds_dict);
}
auto value = CefValue::Create();
value->SetDictionary(dict);
return value;
}
CefRefPtr<CefValue> CreateDefaultWindowRestoreValue() {
return CreateWindowRestoreValue(CEF_SHOW_STATE_NORMAL, std::nullopt);
}
// Parse the CefValue representation that was stored in preferences.
bool ParseWindowRestoreValue(CefRefPtr<CefValue> value,
cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds) {
if (!value || value->GetType() != VTYPE_DICTIONARY) {
return false;
}
auto dict = value->GetDictionary();
bool has_state = false;
// Show state is required.
if (dict->GetType(kWindowRestoreStateKey) == VTYPE_STRING) {
auto result = ShowStateFromString(dict->GetString(kWindowRestoreStateKey));
if (result) {
show_state = *result;
has_state = true;
}
}
// Bounds is optional.
if (has_state && dict->GetType(kWindowRestoreBoundsKey) == VTYPE_DICTIONARY) {
auto bounds_dict = dict->GetDictionary(kWindowRestoreBoundsKey);
if (bounds_dict->GetType(kWindowRestoreBoundsKey_X) == VTYPE_INT &&
bounds_dict->GetType(kWindowRestoreBoundsKey_Y) == VTYPE_INT &&
bounds_dict->GetType(kWindowRestoreBoundsKey_W) == VTYPE_INT &&
bounds_dict->GetType(kWindowRestoreBoundsKey_H) == VTYPE_INT) {
dip_bounds = CefRect(bounds_dict->GetInt(kWindowRestoreBoundsKey_X),
bounds_dict->GetInt(kWindowRestoreBoundsKey_Y),
bounds_dict->GetInt(kWindowRestoreBoundsKey_W),
bounds_dict->GetInt(kWindowRestoreBoundsKey_H));
}
}
return has_state;
}
// Keep the bounds inside the closest display work area.
CefRect ClampBoundsToDisplay(const CefRect& dip_bounds) {
auto display = CefDisplay::GetDisplayMatchingBounds(
dip_bounds, /*input_pixel_coords=*/false);
const auto work_area = display->GetWorkArea();
CefRect bounds = dip_bounds;
if (bounds.width > work_area.width)
bounds.width = work_area.width;
if (bounds.height > work_area.height)
bounds.height = work_area.height;
if (bounds.x < work_area.x)
bounds.x = work_area.x;
else if (bounds.x + bounds.width >= work_area.x + work_area.width)
bounds.x = work_area.x + work_area.width - bounds.width;
if (bounds.y < work_area.y)
bounds.y = work_area.y;
else if (bounds.y + bounds.height >= work_area.y + work_area.height)
bounds.y = work_area.y + work_area.height - bounds.height;
return bounds;
}
} // namespace
void RegisterGlobalPreferences(CefRawPtr<CefPreferenceRegistrar> registrar) {
registrar->AddPreference(kPrefWindowRestore,
CreateDefaultWindowRestoreValue());
}
bool LoadWindowRestorePreferences(cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds) {
CEF_REQUIRE_UI_THREAD();
// Check if show state was specified on the command-line.
auto command_line = CefCommandLine::GetGlobalCommandLine();
if (command_line->HasSwitch(switches::kInitialShowState)) {
auto result = ShowStateFromString(
command_line->GetSwitchValue(switches::kInitialShowState));
if (result) {
show_state = *result;
return true;
}
}
// Check if show state was saved in global preferences.
auto manager = CefPreferenceManager::GetGlobalPreferenceManager();
if (ParseWindowRestoreValue(manager->GetPreference(kPrefWindowRestore),
show_state, dip_bounds)) {
if (dip_bounds) {
// Keep the bounds inside the closest display.
dip_bounds = ClampBoundsToDisplay(*dip_bounds);
}
return true;
}
return false;
}
bool SaveWindowRestorePreferences(cef_show_state_t show_state,
std::optional<CefRect> dip_bounds) {
CEF_REQUIRE_UI_THREAD();
auto manager = CefPreferenceManager::GetGlobalPreferenceManager();
CefString error;
return manager->SetPreference(
kPrefWindowRestore, CreateWindowRestoreValue(show_state, dip_bounds),
error);
}
} // namespace prefs
} // namespace client

View File

@ -0,0 +1,29 @@
// Copyright (c) 2022 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_CLIENT_PREFS_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_PREFS_H_
#pragma once
#include <optional>
#include "include/cef_base.h"
#include "include/cef_preference.h"
namespace client {
namespace prefs {
// Register global preferences with default values.
void RegisterGlobalPreferences(CefRawPtr<CefPreferenceRegistrar> registrar);
// Load/save window restore info.
bool LoadWindowRestorePreferences(cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds);
bool SaveWindowRestorePreferences(cef_show_state_t show_state,
std::optional<CefRect> dip_bounds);
} // namespace prefs
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_PREFS_H_

View File

@ -11,13 +11,7 @@
namespace client {
RootWindowConfig::RootWindowConfig()
: always_on_top(false),
with_controls(true),
with_osr(false),
with_extension(false),
initially_hidden(false),
url(MainContext::Get()->GetMainURL()) {}
RootWindowConfig::RootWindowConfig() : url(MainContext::Get()->GetMainURL()) {}
RootWindow::RootWindow() : delegate_(nullptr) {}

View File

@ -25,19 +25,19 @@ struct RootWindowConfig {
RootWindowConfig();
// If true the window will always display above other windows.
bool always_on_top;
bool always_on_top = false;
// If true the window will show controls.
bool with_controls;
bool with_controls = true;
// If true the window will use off-screen rendering.
bool with_osr;
bool with_osr = false;
// If true the window is hosting an extension app.
bool with_extension;
bool with_extension = false;
// If true the window will be created initially hidden.
bool initially_hidden;
bool initially_hidden = false;
// Requested window position. If |bounds| and |source_bounds| are empty the
// default window size and location will be used.
@ -49,6 +49,10 @@ struct RootWindowConfig {
// based windows when |initially_hidden| is also true.
CefRect source_bounds;
// Requested window show state. This is currently only implemented for Views-
// based windows when |bounds| is non-empty and |initially_hidden| is false.
cef_show_state_t show_state = CEF_SHOW_STATE_NORMAL;
// Parent window. Only used for Views-based windows.
CefRefPtr<CefWindow> parent_window;

View File

@ -11,6 +11,7 @@
#include "include/cef_app.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/cefclient/browser/client_handler_std.h"
#include "tests/cefclient/browser/client_prefs.h"
namespace client {
@ -37,19 +38,11 @@ void RootWindowViews::Init(RootWindow::Delegate* delegate,
delegate_ = delegate;
config_ = std::move(config);
if (config_->initially_hidden && !config_->source_bounds.IsEmpty()) {
// The window will be sized and positioned in OnAutoResize().
initial_bounds_ = config_->source_bounds;
position_on_resize_ = true;
} else {
initial_bounds_ = config_->bounds;
}
CreateClientHandler(config_->url);
initialized_ = true;
// Continue initialization on the UI thread.
InitOnUIThread(settings, config_->url, delegate_->GetRequestContext(this));
InitOnUIThread(settings, delegate_->GetRequestContext(this));
}
void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,
@ -68,7 +61,6 @@ void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,
delegate_ = delegate;
config_ = std::make_unique<RootWindowConfig>();
config_->with_controls = with_controls;
is_popup_ = true;
if (popupFeatures.xSet)
initial_bounds_.x = popupFeatures.x;
@ -217,11 +209,16 @@ CefRefPtr<CefWindow> RootWindowViews::GetParentWindow() {
return config_->parent_window;
}
CefRect RootWindowViews::GetWindowBounds() {
CefRect RootWindowViews::GetInitialBounds() {
CEF_REQUIRE_UI_THREAD();
return initial_bounds_;
}
cef_show_state_t RootWindowViews::GetInitialShowState() {
CEF_REQUIRE_UI_THREAD();
return initial_show_state_;
}
scoped_refptr<ImageCache> RootWindowViews::GetImageCache() {
CEF_REQUIRE_UI_THREAD();
return image_cache_;
@ -239,6 +236,17 @@ void RootWindowViews::OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) {
}
}
void RootWindowViews::OnViewsWindowClosing(CefRefPtr<ViewsWindow> window) {
CEF_REQUIRE_UI_THREAD();
DCHECK(window_);
cef_show_state_t show_state;
std::optional<CefRect> dip_bounds;
if (window_->GetWindowRestorePreferences(show_state, dip_bounds)) {
prefs::SaveWindowRestorePreferences(show_state, dip_bounds);
}
}
void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) {
CEF_REQUIRE_UI_THREAD();
window_ = nullptr;
@ -472,15 +480,32 @@ void RootWindowViews::CreateClientHandler(const std::string& url) {
void RootWindowViews::InitOnUIThread(
const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute this method on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::InitOnUIThread, this,
settings, startup_url, request_context));
settings, request_context));
return;
}
if (config_->initially_hidden && !config_->source_bounds.IsEmpty()) {
// The window will be sized and positioned in OnAutoResize().
initial_bounds_ = config_->source_bounds;
position_on_resize_ = true;
} else if (!config_->bounds.IsEmpty()) {
// Initial state was specified via the config object.
initial_bounds_ = config_->bounds;
initial_show_state_ = config_->show_state;
} else {
// Initial state may be specified via the command-line or global
// preferences.
std::optional<CefRect> bounds;
if (prefs::LoadWindowRestorePreferences(initial_show_state_, bounds) &&
bounds) {
initial_bounds_ = *bounds;
}
}
image_cache_ = delegate_->GetImageCache();
// Populate the default image cache.
@ -490,12 +515,11 @@ void RootWindowViews::InitOnUIThread(
image_cache_->LoadImages(
image_set, base::BindOnce(&RootWindowViews::CreateViewsWindow, this,
settings, startup_url, request_context));
settings, request_context));
}
void RootWindowViews::CreateViewsWindow(
const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context,
const ImageCache::ImageSet& images) {
CEF_REQUIRE_UI_THREAD();
@ -510,7 +534,7 @@ void RootWindowViews::CreateViewsWindow(
#endif
// Create the ViewsWindow. It will show itself after creation.
ViewsWindow::Create(this, client_handler_, startup_url, settings,
ViewsWindow::Create(this, client_handler_, config_->url, settings,
request_context);
}

View File

@ -54,9 +54,11 @@ class RootWindowViews : public RootWindow,
bool WithExtension() override;
bool InitiallyHidden() override;
CefRefPtr<CefWindow> GetParentWindow() override;
CefRect GetWindowBounds() override;
CefRect GetInitialBounds() override;
cef_show_state_t GetInitialShowState() override;
scoped_refptr<ImageCache> GetImageCache() override;
void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) override;
void OnViewsWindowClosing(CefRefPtr<ViewsWindow> window) override;
void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) override;
void OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) override;
ViewsWindow::Delegate* GetDelegateForPopup(
@ -90,10 +92,8 @@ class RootWindowViews : public RootWindow,
void CreateClientHandler(const std::string& url);
void InitOnUIThread(const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context);
void CreateViewsWindow(const CefBrowserSettings& settings,
const std::string& startup_url,
CefRefPtr<CefRequestContext> request_context,
const ImageCache::ImageSet& images);
@ -101,22 +101,20 @@ class RootWindowViews : public RootWindow,
void NotifyViewsWindowActivated();
void NotifyDestroyedIfDone();
// After initialization all members are only accessed on the main thread
// unless otherwise indicated.
// Members set during initialization.
// Members set during initialization. Safe to access from any thread.
std::unique_ptr<RootWindowConfig> config_;
bool is_popup_ = false;
CefRect initial_bounds_;
bool position_on_resize_ = false;
CefRefPtr<ClientHandler> client_handler_;
bool initialized_ = false;
// Only accessed on the main thread.
CefRefPtr<CefBrowser> browser_;
bool window_destroyed_ = false;
bool browser_destroyed_ = false;
CefRefPtr<CefBrowser> browser_;
// Only accessed on the browser process UI thread.
CefRect initial_bounds_;
cef_show_state_t initial_show_state_ = CEF_SHOW_STATE_NORMAL;
bool position_on_resize_ = false;
CefRefPtr<ViewsWindow> window_;
ExtensionSet pending_extensions_;
scoped_refptr<ImageCache> image_cache_;

View File

@ -336,6 +336,32 @@ void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) {
base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded, this, extensions));
}
bool ViewsWindow::GetWindowRestorePreferences(
cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds) {
CEF_REQUIRE_UI_THREAD();
if (!window_)
return false;
show_state = CEF_SHOW_STATE_NORMAL;
if (window_->IsMinimized())
show_state = CEF_SHOW_STATE_MINIMIZED;
else if (window_->IsMaximized())
show_state = CEF_SHOW_STATE_MAXIMIZED;
else if (window_->IsFullscreen())
show_state = CEF_SHOW_STATE_FULLSCREEN;
if (show_state == CEF_SHOW_STATE_NORMAL) {
// Use the current visible bounds.
dip_bounds = window_->GetBoundsInScreen();
} else {
// Use the last known visible bounds.
dip_bounds = last_visible_bounds_;
}
return true;
}
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
@ -517,10 +543,14 @@ void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
delegate_->OnViewsWindowCreated(this);
const CefRect bounds = delegate_->GetWindowBounds();
const CefRect bounds = delegate_->GetInitialBounds();
if (bounds.IsEmpty()) {
// Size the Window and center it at the default size.
window_->CenterWindow(CefSize(kDefaultWidth, kDefaultHeight));
} else {
// Remember the bounds from the previous application run in case the user
// does not move or resize the window during this application run.
last_visible_bounds_ = bounds;
}
// Set the background color for regions that are not obscured by other Views.
@ -539,7 +569,7 @@ void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
AddAccelerators();
// Hide the top controls while in full-screen mode.
if (initial_show_state_ == CEF_SHOW_STATE_FULLSCREEN) {
if (delegate_->GetInitialShowState() == CEF_SHOW_STATE_FULLSCREEN) {
ShowTopControls(false);
}
} else {
@ -558,6 +588,13 @@ void ViewsWindow::OnWindowCreated(CefRefPtr<CefWindow> window) {
}
}
void ViewsWindow::OnWindowClosing(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
DCHECK(window_);
delegate_->OnViewsWindowClosing(this);
}
void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
DCHECK(window_);
@ -577,12 +614,22 @@ void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
void ViewsWindow::OnWindowActivationChanged(CefRefPtr<CefWindow> window,
bool active) {
if (!active)
if (!active) {
return;
}
delegate_->OnViewsWindowActivated(this);
}
void ViewsWindow::OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
const CefRect& new_bounds) {
if (!window->IsMinimized() && !window->IsMaximized() &&
!window->IsFullscreen()) {
// Track the last visible bounds for window restore purposes.
last_visible_bounds_ = new_bounds;
}
}
bool ViewsWindow::CanClose(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
@ -610,7 +657,7 @@ CefRefPtr<CefWindow> ViewsWindow::GetParentWindow(CefRefPtr<CefWindow> window,
CefRect ViewsWindow::GetInitialBounds(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
const CefRect bounds = delegate_->GetWindowBounds();
const CefRect bounds = delegate_->GetInitialBounds();
if (frameless_ && bounds.IsEmpty()) {
// Need to provide a size for frameless windows that will be centered.
return CefRect(0, 0, kDefaultWidth, kDefaultHeight);
@ -620,7 +667,7 @@ CefRect ViewsWindow::GetInitialBounds(CefRefPtr<CefWindow> window) {
cef_show_state_t ViewsWindow::GetInitialShowState(CefRefPtr<CefWindow> window) {
CEF_REQUIRE_UI_THREAD();
return initial_show_state_;
return delegate_->GetInitialShowState();
}
bool ViewsWindow::IsFrameless(CefRefPtr<CefWindow> window) {
@ -807,16 +854,6 @@ ViewsWindow::ViewsWindow(Delegate* delegate,
chrome_toolbar_type_ = CEF_CTT_NONE;
}
const std::string& show_state =
command_line->GetSwitchValue(switches::kInitialShowState);
if (show_state == "minimized") {
initial_show_state_ = CEF_SHOW_STATE_MINIMIZED;
} else if (show_state == "maximized") {
initial_show_state_ = CEF_SHOW_STATE_MAXIMIZED;
} else if (show_state == "fullscreen") {
initial_show_state_ = CEF_SHOW_STATE_FULLSCREEN;
}
#if !defined(OS_MAC)
// On Mac we don't show a top menu on the window. The options are available in
// the app menu instead.

View File

@ -6,6 +6,7 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_WINDOW_H_
#pragma once
#include <optional>
#include <set>
#include <string>
#include <vector>
@ -57,7 +58,10 @@ class ViewsWindow : public CefBrowserViewDelegate,
virtual CefRefPtr<CefWindow> GetParentWindow() = 0;
// Return the initial window bounds.
virtual CefRect GetWindowBounds() = 0;
virtual CefRect GetInitialBounds() = 0;
// Return the initial window show state.
virtual cef_show_state_t GetInitialShowState() = 0;
// Returns the ImageCache.
virtual scoped_refptr<ImageCache> GetImageCache() = 0;
@ -65,6 +69,9 @@ class ViewsWindow : public CefBrowserViewDelegate,
// Called when the ViewsWindow is created.
virtual void OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) = 0;
// Called when the ViewsWindow is closing.
virtual void OnViewsWindowClosing(CefRefPtr<ViewsWindow> window) = 0;
// Called when the ViewsWindow is destroyed. All references to |window|
// should be released in this callback.
virtual void OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) = 0;
@ -121,6 +128,9 @@ class ViewsWindow : public CefBrowserViewDelegate,
void OnBeforeContextMenu(CefRefPtr<CefMenuModel> model);
void OnExtensionsChanged(const ExtensionSet& extensions);
bool GetWindowRestorePreferences(cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds);
// CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
@ -152,9 +162,12 @@ class ViewsWindow : public CefBrowserViewDelegate,
// CefWindowDelegate methods:
void OnWindowCreated(CefRefPtr<CefWindow> window) override;
void OnWindowClosing(CefRefPtr<CefWindow> window) override;
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override;
void OnWindowActivationChanged(CefRefPtr<CefWindow> window,
bool active) override;
void OnWindowBoundsChanged(CefRefPtr<CefWindow> window,
const CefRect& new_bounds) override;
CefRefPtr<CefWindow> GetParentWindow(CefRefPtr<CefWindow> window,
bool* is_menu,
bool* can_activate_menu) override;
@ -235,9 +248,9 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefRefPtr<CefView> location_bar_;
bool menu_has_focus_;
int last_focused_view_;
std::optional<CefRect> last_visible_bounds_;
CefSize minimum_window_size_;
cef_show_state_t initial_show_state_ = CEF_SHOW_STATE_NORMAL;
CefRefPtr<ViewsOverlayControls> overlay_controls_;