From 549e8fe05c06941ee25e06fc0a9ff42f2f701a9b Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Mon, 10 Mar 2025 15:50:46 +0000 Subject: [PATCH] Add visualization for Chrome configuration changes (fixes #3892) - Add new API to retrieve/observe configuration values. - cefclient: Add https://tests/config to inspect configuration values in real time. --- BUILD.gn | 2 + cef_paths2.gypi | 3 + include/cef_preference.h | 64 ++ include/cef_request_context.h | 33 + libcef/browser/context.cc | 25 +- libcef/browser/context.h | 6 + .../browser/global_preference_manager_impl.cc | 89 +++ .../browser/global_preference_manager_impl.h | 3 + libcef/browser/prefs/pref_helper.cc | 142 ++++ libcef/browser/prefs/pref_helper.h | 73 +- libcef/browser/request_context_impl.cc | 38 + libcef/browser/request_context_impl.h | 35 +- libcef/browser/setting_helper.cc | 118 +++ libcef/browser/setting_helper.h | 81 ++ patch/patch.cfg | 9 + patch/patches/config_3892.patch | 85 +++ tests/cefclient/browser/config_test.cc | 334 +++++++++ tests/cefclient/browser/config_test.h | 18 + tests/cefclient/browser/resource.h | 53 +- .../browser/resource_util_win_idmap.cc | 1 + tests/cefclient/browser/test_runner.cc | 4 + tests/cefclient/resources/config.html | 703 ++++++++++++++++++ tests/cefclient/resources/other_tests.html | 1 + tests/cefclient/resources/preferences.html | 6 +- tests/cefclient/win/cefclient.rc | 1 + 25 files changed, 1887 insertions(+), 40 deletions(-) create mode 100644 libcef/browser/setting_helper.cc create mode 100644 libcef/browser/setting_helper.h create mode 100644 patch/patches/config_3892.patch create mode 100644 tests/cefclient/browser/config_test.cc create mode 100644 tests/cefclient/browser/config_test.h create mode 100644 tests/cefclient/resources/config.html diff --git a/BUILD.gn b/BUILD.gn index 199128ba9..8dcd235c7 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -677,6 +677,8 @@ source_set("libcef_static") { "libcef/browser/scheme_impl.cc", "libcef/browser/server_impl.cc", "libcef/browser/server_impl.h", + "libcef/browser/setting_helper.cc", + "libcef/browser/setting_helper.h", "libcef/browser/simple_menu_model_impl.cc", "libcef/browser/simple_menu_model_impl.h", "libcef/browser/ssl_info_impl.cc", diff --git a/cef_paths2.gypi b/cef_paths2.gypi index d923dec71..41be58245 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -247,6 +247,8 @@ 'tests/cefclient/browser/client_prefs.cc', 'tests/cefclient/browser/client_prefs.h', 'tests/cefclient/browser/client_types.h', + 'tests/cefclient/browser/config_test.cc', + 'tests/cefclient/browser/config_test.h', 'tests/cefclient/browser/default_client_handler.cc', 'tests/cefclient/browser/default_client_handler.h', 'tests/cefclient/browser/dialog_test.cc', @@ -324,6 +326,7 @@ 'cefclient_sources_resources': [ 'tests/cefclient/resources/binary_transfer.html', 'tests/cefclient/resources/binding.html', + 'tests/cefclient/resources/config.html', 'tests/cefclient/resources/dialogs.html', 'tests/cefclient/resources/draggable.html', 'tests/cefclient/resources/hang.html', diff --git a/include/cef_preference.h b/include/cef_preference.h index 8f3d61afd..348b6320b 100644 --- a/include/cef_preference.h +++ b/include/cef_preference.h @@ -41,6 +41,7 @@ #include #include "include/cef_base.h" +#include "include/cef_registration.h" #include "include/cef_values.h" /// @@ -65,6 +66,24 @@ class CefPreferenceRegistrar : public CefBaseScoped { CefRefPtr default_value) = 0; }; +#if CEF_API_ADDED(CEF_NEXT) +/// +/// Implemented by the client to observe preference changes and registered via +/// CefPreferenceManager::AddPreferenceObserver. The methods of this class will +/// be called on the browser process UI thread. +/// +/*--cef(source=client,added=next)--*/ +class CefPreferenceObserver : public virtual CefBaseRefCounted { + public: + /// + /// Called when a preference has changed. The new value can be retrieved using + /// CefPreferenceManager::GetPreference. + /// + /*--cef()--*/ + virtual void OnPreferenceChanged(const CefString& name) = 0; +}; +#endif + /// /// Manage access to preferences. Many built-in preferences are registered by /// Chromium. Custom preferences can be registered in @@ -73,6 +92,36 @@ class CefPreferenceRegistrar : public CefBaseScoped { /*--cef(source=library,no_debugct_check)--*/ class CefPreferenceManager : public virtual CefBaseRefCounted { public: +#if CEF_API_ADDED(CEF_NEXT) + /// + /// Returns the current Chrome Variations configuration (combination of field + /// trials and chrome://flags) as equivalent command-line switches + /// (`--[enable|disable]-features=XXXX`, etc). These switches can be used to + /// apply the same configuration when launching a CEF-based application. See + /// https://developer.chrome.com/docs/web-platform/chrome-variations for + /// background and details. Note that field trial tests are disabled by + /// default in Official CEF builds (via the + /// `disable_fieldtrial_testing_config=true` GN flag). This method must be + /// called on the browser process UI thread. + /// + /*--cef(added=next)--*/ + static void GetChromeVariationsAsSwitches(std::vector& switches); + + /// + /// Returns the current Chrome Variations configuration (combination of field + /// trials and chrome://flags) as human-readable strings. This is the + /// human-readable equivalent of the "Active Variations" section of + /// chrome://version. See + /// https://developer.chrome.com/docs/web-platform/chrome-variations for + /// background and details. Note that field trial tests are disabled by + /// default in Official CEF builds (via the + /// `disable_fieldtrial_testing_config=true` GN flag). This method must be + /// called on the browser process UI thread. + /// + /*--cef(added=next)--*/ + static void GetChromeVariationsAsStrings(std::vector& strings); +#endif + /// /// Returns the global preference manager object. /// @@ -129,6 +178,21 @@ class CefPreferenceManager : public virtual CefBaseRefCounted { virtual bool SetPreference(const CefString& name, CefRefPtr value, CefString& error) = 0; + +#if CEF_API_ADDED(CEF_NEXT) + /// + /// Add an observer for preference changes. |name| is the name of the + /// preference to observe. If |name| is empty then all preferences will + /// be observed. Observing all preferences has performance consequences and + /// is not recommended outside of testing scenarios. The observer will remain + /// registered until the returned Registration object is destroyed. This + /// method must be called on the browser process UI thread. + /// + /*--cef(optional_param=name,added=next)--*/ + virtual CefRefPtr AddPreferenceObserver( + const CefString& name, + CefRefPtr observer) = 0; +#endif }; #endif // CEF_INCLUDE_CEF_PREFERENCE_H_ diff --git a/include/cef_request_context.h b/include/cef_request_context.h index 56d3610bd..406649702 100644 --- a/include/cef_request_context.h +++ b/include/cef_request_context.h @@ -44,6 +44,7 @@ #include "include/cef_cookie.h" #include "include/cef_media_router.h" #include "include/cef_preference.h" +#include "include/cef_registration.h" #include "include/cef_values.h" class CefRequestContextHandler; @@ -66,6 +67,27 @@ class CefResolveCallback : public virtual CefBaseRefCounted { const std::vector& resolved_ips) = 0; }; +#if CEF_API_ADDED(CEF_NEXT) +/// +/// Implemented by the client to observe content and website setting changes and +/// registered via CefRequestContext::AddSettingObserver. The methods of this +/// class will be called on the browser process UI thread. +/// +/*--cef(source=client,added=next)--*/ +class CefSettingObserver : public virtual CefBaseRefCounted { + public: + /// + /// Called when a content or website setting has changed. The new value can be + /// retrieved using CefRequestContext::GetContentSetting or + /// CefRequestContext::GetWebsiteSetting. + /// + /*--cef(optional_param=requesting_url,optional_param=top_level_url)--*/ + virtual void OnSettingChanged(const CefString& requesting_url, + const CefString& top_level_url, + cef_content_setting_types_t content_type) = 0; +}; +#endif + /// /// A request context provides request handling for a set of related browser /// or URL request objects. A request context can be specified when creating a @@ -292,6 +314,17 @@ class CefRequestContext : public CefPreferenceManager { cef_content_setting_types_t content_type, cef_content_setting_values_t value) = 0; +#if CEF_API_ADDED(CEF_NEXT) + /// + /// Add an observer for content and website setting changes. The observer will + /// remain registered until the returned Registration object is destroyed. + /// This method must be called on the browser process UI thread. + /// + /*--cef(added=next)--*/ + virtual CefRefPtr AddSettingObserver( + CefRefPtr observer) = 0; +#endif + /// /// Sets the Chrome color scheme for all browsers that share this request /// context. |variant| values of SYSTEM, LIGHT and DARK change the underlying diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index 2c16b72d2..fc1abef02 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -12,10 +12,12 @@ #include "base/task/current_thread.h" #include "base/threading/thread_restrictions.h" #include "cef/libcef/browser/browser_info_manager.h" +#include "cef/libcef/browser/prefs/pref_helper.h" #include "cef/libcef/browser/request_context_impl.h" #include "cef/libcef/browser/thread_util.h" #include "cef/libcef/browser/trace_subscriber.h" #include "cef/libcef/common/cef_switches.h" +#include "chrome/browser/browser_process_impl.h" #include "components/network_session_configurator/common/network_switches.h" #include "ui/base/ui_base_switches.h" @@ -570,12 +572,24 @@ CefTraceSubscriber* CefContext::GetTraceSubscriber() { if (shutting_down_) { return nullptr; } - if (!trace_subscriber_.get()) { + if (!trace_subscriber_) { trace_subscriber_ = std::make_unique(); } return trace_subscriber_.get(); } +pref_helper::Registrar* CefContext::GetPrefRegistrar() { + CEF_REQUIRE_UIT(); + if (shutting_down_) { + return nullptr; + } + if (!pref_registrar_) { + pref_registrar_ = std::make_unique(); + pref_registrar_->Init(g_browser_process->local_state()); + } + return pref_registrar_.get(); +} + void CefContext::PopulateGlobalRequestContextSettings( CefRequestContextSettings* settings) { CefRefPtr command_line = @@ -645,12 +659,15 @@ void CefContext::ShutdownOnUIThread() { observer.OnContextDestroyed(); } - if (trace_subscriber_.get()) { - trace_subscriber_.reset(nullptr); + if (trace_subscriber_) { + trace_subscriber_.reset(); + } + if (pref_registrar_) { + pref_registrar_.reset(); } } void CefContext::FinalizeShutdown() { - browser_info_manager_.reset(nullptr); + browser_info_manager_.reset(); application_ = nullptr; } diff --git a/libcef/browser/context.h b/libcef/browser/context.h index d62853dec..a9aec0a85 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -16,6 +16,10 @@ #include "cef/libcef/browser/main_runner.h" #include "third_party/skia/include/core/SkColor.h" +namespace pref_helper { +class Registrar; +} + class CefBrowserInfoManager; class CefTraceSubscriber; @@ -73,6 +77,7 @@ class CefContext { cef_state_t windowless_state) const; CefTraceSubscriber* GetTraceSubscriber(); + pref_helper::Registrar* GetPrefRegistrar(); // Populate request context settings for the global system context based on // CefSettings and command-line flags. @@ -112,6 +117,7 @@ class CefContext { std::unique_ptr main_runner_; std::unique_ptr trace_subscriber_; + std::unique_ptr pref_registrar_; std::unique_ptr browser_info_manager_; // Observers that want to be notified of changes to this object. diff --git a/libcef/browser/global_preference_manager_impl.cc b/libcef/browser/global_preference_manager_impl.cc index 9a718c6b1..1a3a0a18f 100644 --- a/libcef/browser/global_preference_manager_impl.cc +++ b/libcef/browser/global_preference_manager_impl.cc @@ -4,10 +4,28 @@ #include "cef/libcef/browser/global_preference_manager_impl.h" +#include "base/metrics/field_trial_list_including_low_anonymity.h" +#include "base/strings/string_util.h" #include "cef/libcef/browser/context.h" #include "cef/libcef/browser/prefs/pref_helper.h" #include "cef/libcef/browser/thread_util.h" +#include "cef/libcef/common/api_version_util.h" +#include "chrome/browser/about_flags.h" #include "chrome/browser/browser_process.h" +#include "components/flags_ui/pref_service_flags_storage.h" +#include "components/variations/synthetic_trials_active_group_id_provider.h" + +namespace { + +std::string GetActiveGroupNameAsString( + const base::FieldTrial::ActiveGroup& group) { + constexpr std::string_view kNonBreakingHyphenUTF8 = "\xE2\x80\x91"; + std::string result = group.trial_name + ":" + group.group_name; + base::ReplaceChars(result, "-", kNonBreakingHyphenUTF8, &result); + return result; +} + +} // namespace bool CefGlobalPreferenceManagerImpl::HasPreference(const CefString& name) { CEF_REQUIRE_UIT_RETURN(false); @@ -40,6 +58,77 @@ bool CefGlobalPreferenceManagerImpl::SetPreference(const CefString& name, value, error); } +CefRefPtr +CefGlobalPreferenceManagerImpl::AddPreferenceObserver( + const CefString& name, + CefRefPtr observer) { + CEF_API_REQUIRE_ADDED(CEF_NEXT); + CEF_REQUIRE_UIT_RETURN(nullptr); + return CefContext::Get()->GetPrefRegistrar()->AddObserver(name, observer); +} + +// static +void CefPreferenceManager::GetChromeVariationsAsSwitches( + std::vector& switches) { + CEF_API_REQUIRE_ADDED(CEF_NEXT); + + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + DCHECK(false) << "context not valid"; + return; + } + + switches.clear(); + + // Based on ChromeFeatureListCreator::ConvertFlagsToSwitches(). + + flags_ui::PrefServiceFlagsStorage flags_storage( + g_browser_process->local_state()); + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + about_flags::ConvertFlagsToSwitches(&flags_storage, &command_line, + flags_ui::kNoSentinels); + + for (const auto& arg : command_line.argv()) { + if (!arg.empty()) { + switches.push_back(arg); + } + } +} + +// static +void CefPreferenceManager::GetChromeVariationsAsStrings( + std::vector& strings) { + CEF_API_REQUIRE_ADDED(CEF_NEXT); + + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + DCHECK(false) << "context not valid"; + return; + } + + strings.clear(); + + // Based on components/webui/version/version_handler_helper.cc + // GetVariationsList(). + + base::FieldTrial::ActiveGroups active_groups; + // Include low anonymity trial groups in the version string, as it is only + // displayed locally (and is useful for diagnostics purposes). + base::FieldTrialListIncludingLowAnonymity:: + GetActiveFieldTrialGroupsForTesting(&active_groups); + + for (const auto& group : active_groups) { + strings.push_back(GetActiveGroupNameAsString(group)); + } + + // Synthetic field trials. + for (const auto& group : + variations::SyntheticTrialsActiveGroupIdProvider::GetInstance() + ->GetGroups()) { + strings.push_back(GetActiveGroupNameAsString(group.active_group())); + } +} + // static CefRefPtr CefPreferenceManager::GetGlobalPreferenceManager() { diff --git a/libcef/browser/global_preference_manager_impl.h b/libcef/browser/global_preference_manager_impl.h index 47551af81..597149af7 100644 --- a/libcef/browser/global_preference_manager_impl.h +++ b/libcef/browser/global_preference_manager_impl.h @@ -27,6 +27,9 @@ class CefGlobalPreferenceManagerImpl : public CefPreferenceManager { bool SetPreference(const CefString& name, CefRefPtr value, CefString& error) override; + CefRefPtr AddPreferenceObserver( + const CefString& name, + CefRefPtr observer) override; private: IMPLEMENT_REFCOUNTING(CefGlobalPreferenceManagerImpl); diff --git a/libcef/browser/prefs/pref_helper.cc b/libcef/browser/prefs/pref_helper.cc index be1eb8949..c2a2e1e97 100644 --- a/libcef/browser/prefs/pref_helper.cc +++ b/libcef/browser/prefs/pref_helper.cc @@ -6,6 +6,7 @@ #include "base/notreached.h" #include "base/strings/stringprintf.h" +#include "cef/include/cef_preference.h" #include "cef/libcef/browser/thread_util.h" #include "cef/libcef/common/values_impl.h" #include "components/prefs/pref_service.h" @@ -118,4 +119,145 @@ bool SetPreference(PrefService* pref_service, return true; } +class RegistrationImpl final : public Registration, public CefRegistration { + public: + RegistrationImpl(Registrar* registrar, + const CefString& name, + CefRefPtr observer) + : registrar_(registrar), name_(name), observer_(observer) { + DCHECK(registrar_); + DCHECK(observer_); + } + + RegistrationImpl(const RegistrationImpl&) = delete; + RegistrationImpl& operator=(const RegistrationImpl&) = delete; + + ~RegistrationImpl() override { + CEF_REQUIRE_UIT(); + if (registrar_) { + registrar_->RemoveObserver(name_.ToString(), this); + } + } + + void Detach() override { + registrar_ = nullptr; + observer_ = nullptr; + } + + void RunCallback() const override { RunCallback(name_); } + + void RunCallback(const CefString& name) const override { + observer_->OnPreferenceChanged(name); + } + + private: + raw_ptr registrar_; + CefString name_; + CefRefPtr observer_; + + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(RegistrationImpl); +}; + +Registrar::~Registrar() { + RemoveAll(); +} + +void Registrar::Init(PrefService* service) { + DCHECK(service); + DCHECK(IsEmpty() || service_ == service); + service_ = service; +} + +void Registrar::Reset() { + RemoveAll(); + service_ = nullptr; +} + +void Registrar::RemoveAll() { + if (!name_observers_.empty()) { + for (auto& [name, registrations] : name_observers_) { + service_->RemovePrefObserver(name, this); + for (auto& registration : registrations) { + registration.Detach(); + } + } + name_observers_.clear(); + } + + if (!all_observers_.empty()) { + service_->RemovePrefObserverAllPrefs(this); + for (auto& registration : all_observers_) { + registration.Detach(); + } + all_observers_.Clear(); + } +} + +bool Registrar::IsEmpty() const { + return name_observers_.empty() && all_observers_.empty(); +} + +CefRefPtr Registrar::AddObserver( + const CefString& name, + CefRefPtr observer) { + CHECK(service_); + + RegistrationImpl* impl = new RegistrationImpl(this, name, observer); + + if (name.empty()) { + if (all_observers_.empty()) { + service_->AddPrefObserverAllPrefs(this); + } + all_observers_.AddObserver(impl); + } else { + const std::string& name_str = name.ToString(); + if (!name_observers_.contains(name_str)) { + service_->AddPrefObserver(name_str, this); + } + name_observers_[name_str].AddObserver(impl); + } + + return impl; +} + +void Registrar::RemoveObserver(std::string_view name, + Registration* registration) { + CHECK(service_); + + if (name.empty()) { + all_observers_.RemoveObserver(registration); + if (all_observers_.empty()) { + service_->RemovePrefObserverAllPrefs(this); + } + } else { + auto it = name_observers_.find(std::string(name)); + DCHECK(it != name_observers_.end()); + it->second.RemoveObserver(registration); + if (it->second.empty()) { + name_observers_.erase(it); + service_->RemovePrefObserver(name, this); + } + } +} + +void Registrar::OnPreferenceChanged(PrefService* service, + std::string_view pref_name) { + std::string pref_name_str(pref_name); + if (!name_observers_.empty()) { + auto it = name_observers_.find(pref_name_str); + if (it != name_observers_.end()) { + for (Registration& registration : it->second) { + registration.RunCallback(); + } + } + } + + if (!all_observers_.empty()) { + CefString name_str(pref_name_str); + for (Registration& registration : all_observers_) { + registration.RunCallback(name_str); + } + } +} + } // namespace pref_helper diff --git a/libcef/browser/prefs/pref_helper.h b/libcef/browser/prefs/pref_helper.h index d8ac2a5a4..4ec5cd843 100644 --- a/libcef/browser/prefs/pref_helper.h +++ b/libcef/browser/prefs/pref_helper.h @@ -5,8 +5,16 @@ #ifndef CEF_LIBCEF_BROWSER_PREFS_PREF_HELPER_H_ #define CEF_LIBCEF_BROWSER_PREFS_PREF_HELPER_H_ -#include "cef/include/cef_values.h" +#include +#include "base/memory/raw_ptr.h" +#include "base/observer_list.h" +#include "cef/include/cef_registration.h" +#include "cef/include/cef_values.h" +#include "components/prefs/pref_observer.h" + +class CefPreferenceObserver; +class CefRegistration; class PrefService; namespace pref_helper { @@ -28,6 +36,69 @@ bool SetPreference(PrefService* pref_service, CefRefPtr value, CefString& error); +class Registration : public base::CheckedObserver { + public: + virtual void Detach() = 0; + virtual void RunCallback() const = 0; + virtual void RunCallback(const CefString& name) const = 0; +}; + +class RegistrationImpl; + +// Automatically manages the registration of one or more CefPreferenceObserver +// objects with a PrefService. When the Registrar is destroyed, all registered +// observers are automatically unregistered with the PrefService. Loosely based +// on PrefChangeRegistrar. +class Registrar final : public PrefObserver { + public: + Registrar() = default; + + Registrar(const Registrar&) = delete; + Registrar& operator=(const Registrar&) = delete; + + ~Registrar(); + + // Must be called before adding or removing observers. Can be called more + // than once as long as the value of |service| doesn't change. + void Init(PrefService* service); + + // Removes all observers and clears the reference to the PrefService. + // `Init` must be called before adding or removing any observers. + void Reset(); + + // Removes all observers that have been previously added with a call to Add. + void RemoveAll(); + + // Returns true if no observers are registered. + bool IsEmpty() const; + + // Adds a pref |observer| for the specified pref |name|. All registered + // observers will be automatically unregistered and detached when the + // Registrar's destructor is called. + CefRefPtr AddObserver( + const CefString& name, + CefRefPtr observer); + + private: + friend class RegistrationImpl; + + void RemoveObserver(std::string_view name, Registration* registration); + + // PrefObserver: + void OnPreferenceChanged(PrefService* service, + std::string_view pref_name) override; + + raw_ptr service_ = nullptr; + + // Observers registered for a preference by name. + using ObserverMap = + std::unordered_map>; + ObserverMap name_observers_; + + // Observers registered for all preferences. + base::ObserverList all_observers_; +}; + } // namespace pref_helper #endif // CEF_LIBCEF_BROWSER_PREFS_PREF_HELPER_H_ diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index 0be80fc37..1a8f43b15 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -10,7 +10,9 @@ #include "cef/libcef/browser/browser_context.h" #include "cef/libcef/browser/context.h" #include "cef/libcef/browser/prefs/pref_helper.h" +#include "cef/libcef/browser/setting_helper.h" #include "cef/libcef/browser/thread_util.h" +#include "cef/libcef/common/api_version_util.h" #include "cef/libcef/common/app_manager.h" #include "cef/libcef/common/task_runner_impl.h" #include "cef/libcef/common/values_impl.h" @@ -459,6 +461,22 @@ bool CefRequestContextImpl::SetPreference(const CefString& name, return pref_helper::SetPreference(pref_service, name, value, error); } +CefRefPtr CefRequestContextImpl::AddPreferenceObserver( + const CefString& name, + CefRefPtr observer) { + CEF_API_REQUIRE_ADDED(CEF_NEXT); + if (!VerifyBrowserContext()) { + return nullptr; + } + + if (!pref_registrar_) { + pref_registrar_ = std::make_unique(); + pref_registrar_->Init(browser_context()->AsProfile()->GetPrefs()); + } + + return pref_registrar_->AddObserver(name, observer); +} + void CefRequestContextImpl::ClearCertificateExceptions( CefRefPtr callback) { GetBrowserContext( @@ -588,6 +606,26 @@ void CefRequestContextImpl::SetContentSetting( requesting_url, top_level_url, content_type, value)); } +CefRefPtr CefRequestContextImpl::AddSettingObserver( + CefRefPtr observer) { + CEF_API_REQUIRE_ADDED(CEF_NEXT); + if (!VerifyBrowserContext()) { + return nullptr; + } + + if (!setting_registrar_) { + auto* settings_map = HostContentSettingsMapFactory::GetForProfile( + browser_context()->AsProfile()); + if (!settings_map) { + return nullptr; + } + setting_registrar_ = std::make_unique(); + setting_registrar_->Init(settings_map); + } + + return setting_registrar_->AddObserver(observer); +} + void CefRequestContextImpl::SetChromeColorScheme(cef_color_variant_t variant, cef_color_t user_color) { GetBrowserContext( diff --git a/libcef/browser/request_context_impl.h b/libcef/browser/request_context_impl.h index c70ebd206..f7f078a96 100644 --- a/libcef/browser/request_context_impl.h +++ b/libcef/browser/request_context_impl.h @@ -17,6 +17,14 @@ namespace content { struct GlobalRenderFrameHostId; } +namespace pref_helper { +class Registrar; +} + +namespace setting_helper { +class Registrar; +} + class CefBrowserContext; // Implementation of the CefRequestContext interface. All methods are thread- @@ -80,6 +88,20 @@ class CefRequestContextImpl : public CefRequestContext { scoped_refptr task_runner, BrowserContextCallback callback); + // CefPreferenceManager methods. + bool HasPreference(const CefString& name) override; + CefRefPtr GetPreference(const CefString& name) override; + CefRefPtr GetAllPreferences( + bool include_defaults) override; + bool CanSetPreference(const CefString& name) override; + bool SetPreference(const CefString& name, + CefRefPtr value, + CefString& error) override; + CefRefPtr AddPreferenceObserver( + const CefString& name, + CefRefPtr observer) override; + + // CefRequestContext methods. bool IsSame(CefRefPtr other) override; bool IsSharingWith(CefRefPtr other) override; bool IsGlobal() override; @@ -92,14 +114,6 @@ class CefRequestContextImpl : public CefRequestContext { const CefString& domain_name, CefRefPtr factory) override; bool ClearSchemeHandlerFactories() override; - bool HasPreference(const CefString& name) override; - CefRefPtr GetPreference(const CefString& name) override; - CefRefPtr GetAllPreferences( - bool include_defaults) override; - bool CanSetPreference(const CefString& name) override; - bool SetPreference(const CefString& name, - CefRefPtr value, - CefString& error) override; void ClearCertificateExceptions( CefRefPtr callback) override; void ClearHttpAuthCredentials( @@ -125,6 +139,8 @@ class CefRequestContextImpl : public CefRequestContext { const CefString& top_level_url, cef_content_setting_types_t content_type, cef_content_setting_values_t value) override; + CefRefPtr AddSettingObserver( + CefRefPtr observer) override; void SetChromeColorScheme(cef_color_variant_t variant, cef_color_t user_color) override; cef_color_variant_t GetChromeColorSchemeMode() override; @@ -218,6 +234,9 @@ class CefRequestContextImpl : public CefRequestContext { Config config_; + std::unique_ptr pref_registrar_; + std::unique_ptr setting_registrar_; + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRequestContextImpl); }; diff --git a/libcef/browser/setting_helper.cc b/libcef/browser/setting_helper.cc new file mode 100644 index 000000000..78a07f34b --- /dev/null +++ b/libcef/browser/setting_helper.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2025 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 "cef/libcef/browser/setting_helper.h" + +#include "cef/include/cef_request_context.h" +#include "cef/libcef/browser/thread_util.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "url/gurl.h" + +namespace setting_helper { + +class RegistrationImpl final : public Registration, public CefRegistration { + public: + RegistrationImpl(Registrar* registrar, CefRefPtr observer) + : registrar_(registrar), observer_(observer) { + DCHECK(registrar_); + DCHECK(observer_); + } + + RegistrationImpl(const RegistrationImpl&) = delete; + RegistrationImpl& operator=(const RegistrationImpl&) = delete; + + ~RegistrationImpl() override { + CEF_REQUIRE_UIT(); + if (registrar_) { + registrar_->RemoveObserver(this); + } + } + + void Detach() override { + registrar_ = nullptr; + observer_ = nullptr; + } + + void RunCallback(const CefString& requesting_url, + const CefString& top_level_url, + cef_content_setting_types_t content_type) const override { + observer_->OnSettingChanged(requesting_url, top_level_url, content_type); + } + + private: + raw_ptr registrar_; + CefRefPtr observer_; + + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(RegistrationImpl); +}; + +Registrar::~Registrar() { + RemoveAll(); +} + +void Registrar::Init(HostContentSettingsMap* settings) { + DCHECK(settings); + DCHECK(IsEmpty() || settings_ == settings); + settings_ = settings; +} + +void Registrar::Reset() { + RemoveAll(); + settings_ = nullptr; +} + +void Registrar::RemoveAll() { + if (!observers_.empty()) { + settings_->RemoveObserver(this); + for (auto& registration : observers_) { + registration.Detach(); + } + observers_.Clear(); + } +} + +bool Registrar::IsEmpty() const { + return observers_.empty(); +} + +CefRefPtr Registrar::AddObserver( + CefRefPtr observer) { + CHECK(settings_); + + RegistrationImpl* impl = new RegistrationImpl(this, observer); + + if (observers_.empty()) { + settings_->AddObserver(this); + } + observers_.AddObserver(impl); + + return impl; +} + +void Registrar::RemoveObserver(Registration* registration) { + CHECK(settings_); + + observers_.RemoveObserver(registration); + if (observers_.empty()) { + settings_->RemoveObserver(this); + } +} + +void Registrar::OnContentSettingChanged( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsTypeSet content_type_set) { + DCHECK(!IsEmpty()); + + const CefString requesting_url(primary_pattern.ToRepresentativeUrl().spec()); + const CefString top_level_url(secondary_pattern.ToRepresentativeUrl().spec()); + const auto content_type = + static_cast(content_type_set.GetType()); + + for (Registration& registration : observers_) { + registration.RunCallback(requesting_url, top_level_url, content_type); + } +} + +} // namespace setting_helper diff --git a/libcef/browser/setting_helper.h b/libcef/browser/setting_helper.h new file mode 100644 index 000000000..e8f702910 --- /dev/null +++ b/libcef/browser/setting_helper.h @@ -0,0 +1,81 @@ +// Copyright (c) 2025 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_LIBCEF_BROWSER_SETTING_HELPER_H_ +#define CEF_LIBCEF_BROWSER_SETTING_HELPER_H_ + +#include "base/memory/raw_ptr.h" +#include "base/observer_list.h" +#include "cef/include/cef_registration.h" +#include "components/content_settings/core/browser/content_settings_observer.h" + +class CefSettingObserver; +class CefRegistration; +class HostContentSettingsMap; + +namespace setting_helper { + +class Registration : public base::CheckedObserver { + public: + virtual void Detach() = 0; + virtual void RunCallback(const CefString& requesting_url, + const CefString& top_level_url, + cef_content_setting_types_t content_type) const = 0; +}; + +class RegistrationImpl; + +// Automatically manages the registration of one or more CefSettingObserver +// objects with a HostContentSettingsMap. When the Registrar is destroyed, all +// registered observers are automatically unregistered with the +// HostContentSettingsMap. Loosely based on PrefChangeRegistrar. +class Registrar final : public content_settings::Observer { + public: + Registrar() = default; + + Registrar(const Registrar&) = delete; + Registrar& operator=(const Registrar&) = delete; + + ~Registrar(); + + // Must be called before adding or removing observers. Can be called more + // than once as long as the value of |settings| doesn't change. + void Init(HostContentSettingsMap* settings); + + // Removes all observers and clears the reference to the + // HostContentSettingsMap. `Init` must be called before adding or removing any + // observers. + void Reset(); + + // Removes all observers that have been previously added with a call to Add. + void RemoveAll(); + + // Returns true if no observers are registered. + bool IsEmpty() const; + + // Adds a setting observer. All registered observers will be automatically + // unregistered and detached when the Registrar's destructor is called. + CefRefPtr AddObserver( + CefRefPtr observer); + + private: + friend class RegistrationImpl; + + void RemoveObserver(Registration* registration); + + // content_settings::Observer: + void OnContentSettingChanged( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsTypeSet content_type_set) override; + + raw_ptr settings_ = + nullptr; + + base::ObserverList observers_; +}; + +} // namespace setting_helper + +#endif // CEF_LIBCEF_BROWSER_SETTING_HELPER_H_ diff --git a/patch/patch.cfg b/patch/patch.cfg index 3b864ac06..66c3940f7 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -764,5 +764,14 @@ patches = [ # Expose Mojo Connector error state to Receiver disconnect handlers. # https://github.com/chromiumembedded/cef/issues/3664 'name': 'mojo_connect_result_3664' + }, + { + # Support for configuration accessors/observers. + # - Allow pref_service::Registrar access to + # PrefService::[Add|Remove]PrefObserver. + # - Enable SyntheticTrialsActiveGroupIdProvider::GetGroups in Release + # builds. + # https://github.com/chromiumembedded/cef/issues/3892 + 'name': 'config_3892' } ] diff --git a/patch/patches/config_3892.patch b/patch/patches/config_3892.patch new file mode 100644 index 000000000..939fe0e13 --- /dev/null +++ b/patch/patches/config_3892.patch @@ -0,0 +1,85 @@ +diff --git components/prefs/pref_service.h components/prefs/pref_service.h +index f1856d6ee4419..413b77e80d84d 100644 +--- components/prefs/pref_service.h ++++ components/prefs/pref_service.h +@@ -52,6 +52,10 @@ namespace base { + class FilePath; + } + ++namespace pref_helper { ++class Registrar; ++} ++ + namespace prefs { + class ScopedDictionaryPrefUpdate; + } +@@ -441,6 +445,8 @@ class COMPONENTS_PREFS_EXPORT PrefService { + // declared as a friend, too. + friend class PrefChangeRegistrar; + friend class subtle::PrefMemberBase; ++ // CEF registration manager. ++ friend class pref_helper::Registrar; + + // These are protected so they can only be accessed by the friend + // classes listed above. +diff --git components/variations/synthetic_trials_active_group_id_provider.cc components/variations/synthetic_trials_active_group_id_provider.cc +index bd51697297471..3e669cd080457 100644 +--- components/variations/synthetic_trials_active_group_id_provider.cc ++++ components/variations/synthetic_trials_active_group_id_provider.cc +@@ -27,7 +27,7 @@ SyntheticTrialsActiveGroupIdProvider::GetActiveGroupIds() { + return group_ids_; + } + +-#if !defined(NDEBUG) ++#if !defined(NDEBUG) || BUILDFLAG(ENABLE_CEF) + std::vector + SyntheticTrialsActiveGroupIdProvider::GetGroups() { + base::AutoLock scoped_lock(lock_); +@@ -38,7 +38,7 @@ SyntheticTrialsActiveGroupIdProvider::GetGroups() { + void SyntheticTrialsActiveGroupIdProvider::ResetForTesting() { + base::AutoLock scoped_lock(lock_); + group_ids_.clear(); +-#if !defined(NDEBUG) ++#if !defined(NDEBUG) || BUILDFLAG(ENABLE_CEF) + groups_.clear(); + #endif // !defined(NDEBUG) + } +@@ -53,7 +53,7 @@ void SyntheticTrialsActiveGroupIdProvider::OnSyntheticTrialsChanged( + for (const auto& group : groups) { + group_ids_.push_back(group.id()); + } +-#if !defined(NDEBUG) ++#if !defined(NDEBUG) || BUILDFLAG(ENABLE_CEF) + groups_ = groups; + #endif // !defined(NDEBUG) + } +diff --git components/variations/synthetic_trials_active_group_id_provider.h components/variations/synthetic_trials_active_group_id_provider.h +index b4a999731d996..0bbac173fddf1 100644 +--- components/variations/synthetic_trials_active_group_id_provider.h ++++ components/variations/synthetic_trials_active_group_id_provider.h +@@ -10,6 +10,7 @@ + #include "base/component_export.h" + #include "base/synchronization/lock.h" + #include "base/thread_annotations.h" ++#include "cef/libcef/features/features.h" + #include "components/variations/active_field_trials.h" + #include "components/variations/synthetic_trials.h" + +@@ -36,7 +37,7 @@ class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialsActiveGroupIdProvider + // Returns currently active synthetic trial group IDs. + std::vector GetActiveGroupIds(); + +-#if !defined(NDEBUG) ++#if !defined(NDEBUG) || BUILDFLAG(ENABLE_CEF) + // In debug mode, not only the group IDs are tracked but also the full group + // info, to display the names unhashed in chrome://version. + std::vector GetGroups(); +@@ -60,7 +61,7 @@ class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialsActiveGroupIdProvider + + base::Lock lock_; + std::vector group_ids_; // GUARDED_BY(lock_); +-#if !defined(NDEBUG) ++#if !defined(NDEBUG) || BUILDFLAG(ENABLE_CEF) + // In debug builds, keep the full group information to be able to display it + // in chrome://version. + std::vector groups_; // GUARDED_BY(lock_); diff --git a/tests/cefclient/browser/config_test.cc b/tests/cefclient/browser/config_test.cc new file mode 100644 index 000000000..58001c889 --- /dev/null +++ b/tests/cefclient/browser/config_test.cc @@ -0,0 +1,334 @@ +// Copyright (c) 2025 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/config_test.h" + +#include +#include +#include +#include + +#include "include/base/cef_logging.h" +#include "include/cef_parser.h" +#include "include/cef_request_context.h" +#include "tests/cefclient/browser/test_runner.h" + +namespace client::config_test { + +namespace { + +const char kTestUrlPath[] = "/config"; + +// Application-specific error codes. +const int kMessageFormatError = 1; +const int kRequestFailedError = 2; + +// Common to all messages. +const char kNameKey[] = "name"; +const char kNameGlobalConfig[] = "global_config"; +const char kNameSubscribe[] = "subscribe"; + +// Convert a dictionary value to a JSON string. +CefString GetJSON(CefRefPtr dictionary) { + CefRefPtr value = CefValue::Create(); + value->SetDictionary(dictionary); + return CefWriteJSON(value, JSON_WRITER_DEFAULT); +} + +using CallbackType = CefMessageRouterBrowserSide::Callback; + +void SendSuccess(CefRefPtr callback, + CefRefPtr result) { + callback->Success(GetJSON(result)); +} + +void SendFailure(CefRefPtr callback, + int error_code, + const std::string& error_message) { + callback->Failure(error_code, error_message); +} + +class PreferenceObserver : public CefPreferenceObserver { + public: + PreferenceObserver(CefRefPtr manager, + bool global, + CefRefPtr callback) + : manager_(manager), global_(global), callback_(callback) {} + + PreferenceObserver(const PreferenceObserver&) = delete; + PreferenceObserver& operator=(const PreferenceObserver&) = delete; + + void OnPreferenceChanged(const CefString& name) override { + CEF_REQUIRE_UI_THREAD(); + auto value = manager_->GetPreference(name); + + auto payload = CefDictionaryValue::Create(); + payload->SetString("type", "preference"); + payload->SetBool("global", global_); + payload->SetString("name", name); + if (value) { + payload->SetInt("value_type", value->GetType()); + payload->SetValue("value", value); + } else { + payload->SetInt("value_type", VTYPE_NULL); + payload->SetNull("value"); + } + + SendSuccess(callback_, payload); + } + + private: + const CefRefPtr manager_; + const bool global_; + const CefRefPtr callback_; + + IMPLEMENT_REFCOUNTING(PreferenceObserver); +}; + +class SettingObserver : public CefSettingObserver { + public: + SettingObserver(CefRefPtr context, + CefRefPtr callback) + : context_(context), callback_(callback) {} + + SettingObserver(const SettingObserver&) = delete; + SettingObserver& operator=(const SettingObserver&) = delete; + + void OnSettingChanged(const CefString& requesting_url, + const CefString& top_level_url, + cef_content_setting_types_t content_type) override { + CEF_REQUIRE_UI_THREAD(); + auto value = context_->GetWebsiteSetting(requesting_url, top_level_url, + content_type); + + auto payload = CefDictionaryValue::Create(); + payload->SetString("type", "setting"); + payload->SetString("requesting_url", requesting_url); + payload->SetString("top_level_url", top_level_url); + payload->SetInt("content_type", static_cast(content_type)); + if (value) { + payload->SetInt("value_type", value->GetType()); + payload->SetValue("value", value); + } else { + payload->SetInt("value_type", VTYPE_NULL); + payload->SetNull("value"); + } + + SendSuccess(callback_, payload); + } + + private: + const CefRefPtr context_; + const CefRefPtr callback_; + + IMPLEMENT_REFCOUNTING(SettingObserver); +}; + +// Handle messages in the browser process. Only accessed on the UI thread. +class Handler : public CefMessageRouterBrowserSide::Handler { + public: + typedef std::vector NameVector; + + Handler() { CEF_REQUIRE_UI_THREAD(); } + + Handler(const Handler&) = delete; + Handler& operator=(const Handler&) = delete; + + ~Handler() override { + for (auto& pair : subscription_state_map_) { + delete pair.second; + } + } + + // Called due to cefQuery execution in config.html. + bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64_t query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) override { + CEF_REQUIRE_UI_THREAD(); + + // Only handle messages from the test URL. + const std::string& url = frame->GetURL(); + if (!test_runner::IsTestURL(url, kTestUrlPath)) { + return false; + } + + // Parse |request| as a JSON dictionary. + CefRefPtr request_dict = ParseJSON(request); + if (!request_dict) { + callback->Failure(kMessageFormatError, "Incorrect message format"); + return true; + } + + // Verify the "name" key. + if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback)) { + return true; + } + + const std::string& message_name = request_dict->GetString(kNameKey); + if (message_name == kNameGlobalConfig) { + // JavaScript is requesting a JSON representation of the global config. + + SendGlobalConfig(callback); + return true; + } else if (message_name == kNameSubscribe) { + // Subscribe to notifications from observers. + + if (!persistent) { + SendFailure(callback, kMessageFormatError, + "Subscriptions must be persistent"); + return true; + } + + if (!CreateSubscription(browser, query_id, callback)) { + SendFailure(callback, kRequestFailedError, + "Browser is already subscribed"); + } + return true; + } + + return false; + } + + void OnQueryCanceled(CefRefPtr browser, + CefRefPtr frame, + int64_t query_id) override { + CEF_REQUIRE_UI_THREAD(); + RemoveSubscription(browser->GetIdentifier(), query_id); + } + + private: + // Convert a JSON string to a dictionary value. + static CefRefPtr ParseJSON(const CefString& string) { + CefRefPtr value = CefParseJSON(string, JSON_PARSER_RFC); + if (value.get() && value->GetType() == VTYPE_DICTIONARY) { + return value->GetDictionary(); + } + return nullptr; + } + + // Verify that |key| exists in |dictionary| and has type |value_type|. Fails + // |callback| and returns false on failure. + static bool VerifyKey(CefRefPtr dictionary, + const char* key, + cef_value_type_t value_type, + CefRefPtr callback) { + if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) { + callback->Failure( + kMessageFormatError, + "Missing or incorrectly formatted message key: " + std::string(key)); + return false; + } + return true; + } + + static CefRefPtr MakeListValue(std::vector vec) { + if (vec.empty()) { + return nullptr; + } + auto list = CefListValue::Create(); + list->SetSize(vec.size()); + size_t idx = 0; + for (const auto& val : vec) { + list->SetString(idx++, val); + } + return list; + } + + void SendGlobalConfig(CefRefPtr callback) { + std::vector switches; + CefPreferenceManager::GetChromeVariationsAsSwitches(switches); + std::vector strings; + CefPreferenceManager::GetChromeVariationsAsStrings(strings); + + auto payload = CefDictionaryValue::Create(); + + if (auto list = MakeListValue(switches)) { + payload->SetList("switches", list); + } else { + payload->SetNull("switches"); + } + + if (auto list = MakeListValue(strings)) { + payload->SetList("strings", list); + } else { + payload->SetNull("strings"); + } + + SendSuccess(callback, payload); + } + + // Subscription state associated with a single browser. + struct SubscriptionState { + int64_t query_id; + CefRefPtr global_pref_observer; + CefRefPtr global_pref_registration; + CefRefPtr context_pref_observer; + CefRefPtr context_pref_registration; + CefRefPtr context_setting_observer; + CefRefPtr context_setting_registration; + }; + + bool CreateSubscription(CefRefPtr browser, + int64_t query_id, + CefRefPtr callback) { + const int browser_id = browser->GetIdentifier(); + if (subscription_state_map_.find(browser_id) != + subscription_state_map_.end()) { + // An subscription already exists for this browser. + return false; + } + + auto global_pref_manager = + CefPreferenceManager::GetGlobalPreferenceManager(); + auto request_context = browser->GetHost()->GetRequestContext(); + + SubscriptionState* state = new SubscriptionState(); + state->query_id = query_id; + + state->global_pref_observer = + new PreferenceObserver(global_pref_manager, /*global=*/true, callback); + state->global_pref_registration = + global_pref_manager->AddPreferenceObserver(CefString(), + state->global_pref_observer); + + state->context_pref_observer = + new PreferenceObserver(request_context, /*global=*/false, callback); + state->context_pref_registration = request_context->AddPreferenceObserver( + CefString(), state->context_pref_observer); + + state->context_setting_observer = + new SettingObserver(request_context, callback); + state->context_setting_registration = + request_context->AddSettingObserver(state->context_setting_observer); + + subscription_state_map_[browser_id] = state; + + return true; + } + + void RemoveSubscription(int browser_id, int64_t query_id) { + SubscriptionStateMap::iterator it = + subscription_state_map_.find(browser_id); + if (it != subscription_state_map_.end() && + it->second->query_id == query_id) { + delete it->second; + subscription_state_map_.erase(it); + } + } + + // Map of browser ID to SubscriptionState object. + typedef std::map SubscriptionStateMap; + SubscriptionStateMap subscription_state_map_; +}; + +} // namespace + +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) { + handlers.insert(new Handler()); +} + +} // namespace client::config_test diff --git a/tests/cefclient/browser/config_test.h b/tests/cefclient/browser/config_test.h new file mode 100644 index 000000000..90dd67830 --- /dev/null +++ b/tests/cefclient/browser/config_test.h @@ -0,0 +1,18 @@ +// Copyright (c) 2025 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_CONFIG_TEST_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_CONFIG_TEST_H_ +#pragma once + +#include "tests/cefclient/browser/test_runner.h" + +namespace client::config_test { + +// Create message handlers. Called from test_runner.cc. +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers); + +} // namespace client::config_test + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_CONFIG_TEST_H_ diff --git a/tests/cefclient/browser/resource.h b/tests/cefclient/browser/resource.h index c1be80bde..d43989eb2 100644 --- a/tests/cefclient/browser/resource.h +++ b/tests/cefclient/browser/resource.h @@ -46,32 +46,33 @@ #define IDC_STATIC -1 #define IDS_BINARY_TRANSFER_HTML 1000 #define IDS_BINDING_HTML 1001 -#define IDS_DIALOGS_HTML 1002 -#define IDS_DRAGGABLE_HTML 1003 -#define IDS_HANG_HTML 1004 -#define IDS_IPC_PERFORMANCE_HTML 1005 -#define IDS_LOCALSTORAGE_HTML 1006 -#define IDS_LOGO_PNG 1007 -#define IDS_MEDIA_ROUTER_HTML 1008 -#define IDS_MENU_ICON_1X_PNG 1009 -#define IDS_MENU_ICON_2X_PNG 1010 -#define IDS_OSRTEST_HTML 1011 -#define IDS_OTHER_TESTS_HTML 1012 -#define IDS_PDF_HTML 1013 -#define IDS_PDF_PDF 1014 -#define IDS_PERFORMANCE_HTML 1015 -#define IDS_PERFORMANCE2_HTML 1016 -#define IDS_PREFERENCES_HTML 1017 -#define IDS_RESPONSE_FILTER_HTML 1018 -#define IDS_SERVER_HTML 1019 -#define IDS_TASK_MANAGER_HTML 1020 -#define IDS_TRANSPARENCY_HTML 1021 -#define IDS_URLREQUEST_HTML 1022 -#define IDS_WEBSOCKET_HTML 1023 -#define IDS_WINDOW_HTML 1024 -#define IDS_WINDOW_ICON_1X_PNG 1025 -#define IDS_WINDOW_ICON_2X_PNG 1026 -#define IDS_XMLHTTPREQUEST_HTML 1027 +#define IDS_CONFIG_HTML 1002 +#define IDS_DIALOGS_HTML 1003 +#define IDS_DRAGGABLE_HTML 1004 +#define IDS_HANG_HTML 1005 +#define IDS_IPC_PERFORMANCE_HTML 1006 +#define IDS_LOCALSTORAGE_HTML 1007 +#define IDS_LOGO_PNG 1008 +#define IDS_MEDIA_ROUTER_HTML 1009 +#define IDS_MENU_ICON_1X_PNG 1010 +#define IDS_MENU_ICON_2X_PNG 1011 +#define IDS_OSRTEST_HTML 1012 +#define IDS_OTHER_TESTS_HTML 1013 +#define IDS_PDF_HTML 1014 +#define IDS_PDF_PDF 1015 +#define IDS_PERFORMANCE_HTML 1016 +#define IDS_PERFORMANCE2_HTML 1017 +#define IDS_PREFERENCES_HTML 1018 +#define IDS_RESPONSE_FILTER_HTML 1019 +#define IDS_SERVER_HTML 1020 +#define IDS_TASK_MANAGER_HTML 1021 +#define IDS_TRANSPARENCY_HTML 1022 +#define IDS_URLREQUEST_HTML 1023 +#define IDS_WEBSOCKET_HTML 1024 +#define IDS_WINDOW_HTML 1025 +#define IDS_WINDOW_ICON_1X_PNG 1026 +#define IDS_WINDOW_ICON_2X_PNG 1027 +#define IDS_XMLHTTPREQUEST_HTML 1028 // Next default values for new objects // diff --git a/tests/cefclient/browser/resource_util_win_idmap.cc b/tests/cefclient/browser/resource_util_win_idmap.cc index 3e22daa61..95652b7bb 100644 --- a/tests/cefclient/browser/resource_util_win_idmap.cc +++ b/tests/cefclient/browser/resource_util_win_idmap.cc @@ -15,6 +15,7 @@ int GetResourceId(const char* resource_name) { int id; } resource_map[] = {{"binary_transfer.html", IDS_BINARY_TRANSFER_HTML}, {"binding.html", IDS_BINDING_HTML}, + {"config.html", IDS_CONFIG_HTML}, {"dialogs.html", IDS_DIALOGS_HTML}, {"draggable.html", IDS_DRAGGABLE_HTML}, {"hang.html", IDS_HANG_HTML}, diff --git a/tests/cefclient/browser/test_runner.cc b/tests/cefclient/browser/test_runner.cc index 9abc22dfc..501e03754 100644 --- a/tests/cefclient/browser/test_runner.cc +++ b/tests/cefclient/browser/test_runner.cc @@ -21,6 +21,7 @@ #include "tests/cefclient/browser/base_client_handler.h" #include "tests/cefclient/browser/binary_transfer_test.h" #include "tests/cefclient/browser/binding_test.h" +#include "tests/cefclient/browser/config_test.h" #include "tests/cefclient/browser/dialog_test.h" #include "tests/cefclient/browser/hang_test.h" #include "tests/cefclient/browser/main_context.h" @@ -865,6 +866,9 @@ void CreateMessageHandlers(MessageHandlerSet& handlers) { // Create the binding test handlers. binding_test::CreateMessageHandlers(handlers); + // Create the config test handlers. + config_test::CreateMessageHandlers(handlers); + // Create the dialog test handlers. dialog_test::CreateMessageHandlers(handlers); diff --git a/tests/cefclient/resources/config.html b/tests/cefclient/resources/config.html new file mode 100644 index 000000000..7750002d9 --- /dev/null +++ b/tests/cefclient/resources/config.html @@ -0,0 +1,703 @@ + + + + Configuration Test + + + + + + + +
+
+ Startup configuration +

+ This section displays the global configuration (Chrome Variations) that was applied at startup. + Chrome Variations can be configured via chrome://flags [*], via the below command-line switches, or via field trials (disabled in Official builds). + The Active Variations section below is the human-readable equivalent of the "Active Variations" section of chrome://version. + See Chrome Variations docs for background. +

+

+ * Flags are stored in the global browser.enabled_labs_experiments preference. +

+

Command-Line Switches:

+

+
+ Active Variations (0) +

+
+
+
+
+ Runtime configuration +

+ This section displays preference and site settings changes that occur during runtime. + Chromium stores both global and Profile-specific preferences. + See Preferences docs for background. + To view a snapshot of all preferences go here instead. +

+

+

+ Text Contains: +
Show: Global preferences + Profile-specific preferences + Site settings [*] +
+ [**] Filtered out: 0 + +

+ * Site settings are stored in the Profile-specific profile.content_settings preference and can be modified via chrome://settings/content. +
** Events will not be displayed or filtered out while processing is paused. +

+
+

+
+
+
+ + diff --git a/tests/cefclient/resources/other_tests.html b/tests/cefclient/resources/other_tests.html index 9e8fcbaec..2b311ba02 100644 --- a/tests/cefclient/resources/other_tests.html +++ b/tests/cefclient/resources/other_tests.html @@ -10,6 +10,7 @@
  • Authentication (Basic) - credentials returned via GetAuthCredentials
  • Authentication (Digest) - credentials returned via GetAuthCredentials
  • Binary vs String Transfer Benchmark
  • +
  • Chrome Configuration
  • Cursors
  • Dialogs
  • Drag & Drop
  • diff --git a/tests/cefclient/resources/preferences.html b/tests/cefclient/resources/preferences.html index 8ebe74690..b960cf9ef 100644 --- a/tests/cefclient/resources/preferences.html +++ b/tests/cefclient/resources/preferences.html @@ -13,8 +13,12 @@