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.
This commit is contained in:
Marshall Greenblatt
2025-03-10 15:50:46 +00:00
parent ec31b2b505
commit 549e8fe05c
25 changed files with 1887 additions and 40 deletions

View File

@@ -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<CefTraceSubscriber>();
}
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_helper::Registrar>();
pref_registrar_->Init(g_browser_process->local_state());
}
return pref_registrar_.get();
}
void CefContext::PopulateGlobalRequestContextSettings(
CefRequestContextSettings* settings) {
CefRefPtr<CefCommandLine> 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;
}

View File

@@ -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<CefMainRunner> main_runner_;
std::unique_ptr<CefTraceSubscriber> trace_subscriber_;
std::unique_ptr<pref_helper::Registrar> pref_registrar_;
std::unique_ptr<CefBrowserInfoManager> browser_info_manager_;
// Observers that want to be notified of changes to this object.

View File

@@ -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<CefRegistration>
CefGlobalPreferenceManagerImpl::AddPreferenceObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> 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<CefString>& 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<CefString>& 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>
CefPreferenceManager::GetGlobalPreferenceManager() {

View File

@@ -27,6 +27,9 @@ class CefGlobalPreferenceManagerImpl : public CefPreferenceManager {
bool SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) override;
CefRefPtr<CefRegistration> AddPreferenceObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> observer) override;
private:
IMPLEMENT_REFCOUNTING(CefGlobalPreferenceManagerImpl);

View File

@@ -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<CefPreferenceObserver> 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> registrar_;
CefString name_;
CefRefPtr<CefPreferenceObserver> 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<CefRegistration> Registrar::AddObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> 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

View File

@@ -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 <unordered_map>
#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<CefValue> 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<CefRegistration> AddObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> 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<PrefService, AcrossTasksDanglingUntriaged> service_ = nullptr;
// Observers registered for a preference by name.
using ObserverMap =
std::unordered_map<std::string, base::ObserverList<Registration>>;
ObserverMap name_observers_;
// Observers registered for all preferences.
base::ObserverList<Registration> all_observers_;
};
} // namespace pref_helper
#endif // CEF_LIBCEF_BROWSER_PREFS_PREF_HELPER_H_

View File

@@ -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<CefRegistration> CefRequestContextImpl::AddPreferenceObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> observer) {
CEF_API_REQUIRE_ADDED(CEF_NEXT);
if (!VerifyBrowserContext()) {
return nullptr;
}
if (!pref_registrar_) {
pref_registrar_ = std::make_unique<pref_helper::Registrar>();
pref_registrar_->Init(browser_context()->AsProfile()->GetPrefs());
}
return pref_registrar_->AddObserver(name, observer);
}
void CefRequestContextImpl::ClearCertificateExceptions(
CefRefPtr<CefCompletionCallback> callback) {
GetBrowserContext(
@@ -588,6 +606,26 @@ void CefRequestContextImpl::SetContentSetting(
requesting_url, top_level_url, content_type, value));
}
CefRefPtr<CefRegistration> CefRequestContextImpl::AddSettingObserver(
CefRefPtr<CefSettingObserver> 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_helper::Registrar>();
setting_registrar_->Init(settings_map);
}
return setting_registrar_->AddObserver(observer);
}
void CefRequestContextImpl::SetChromeColorScheme(cef_color_variant_t variant,
cef_color_t user_color) {
GetBrowserContext(

View File

@@ -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<base::SingleThreadTaskRunner> task_runner,
BrowserContextCallback callback);
// CefPreferenceManager methods.
bool HasPreference(const CefString& name) override;
CefRefPtr<CefValue> GetPreference(const CefString& name) override;
CefRefPtr<CefDictionaryValue> GetAllPreferences(
bool include_defaults) override;
bool CanSetPreference(const CefString& name) override;
bool SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) override;
CefRefPtr<CefRegistration> AddPreferenceObserver(
const CefString& name,
CefRefPtr<CefPreferenceObserver> observer) override;
// CefRequestContext methods.
bool IsSame(CefRefPtr<CefRequestContext> other) override;
bool IsSharingWith(CefRefPtr<CefRequestContext> other) override;
bool IsGlobal() override;
@@ -92,14 +114,6 @@ class CefRequestContextImpl : public CefRequestContext {
const CefString& domain_name,
CefRefPtr<CefSchemeHandlerFactory> factory) override;
bool ClearSchemeHandlerFactories() override;
bool HasPreference(const CefString& name) override;
CefRefPtr<CefValue> GetPreference(const CefString& name) override;
CefRefPtr<CefDictionaryValue> GetAllPreferences(
bool include_defaults) override;
bool CanSetPreference(const CefString& name) override;
bool SetPreference(const CefString& name,
CefRefPtr<CefValue> value,
CefString& error) override;
void ClearCertificateExceptions(
CefRefPtr<CefCompletionCallback> 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<CefRegistration> AddSettingObserver(
CefRefPtr<CefSettingObserver> 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_helper::Registrar> pref_registrar_;
std::unique_ptr<setting_helper::Registrar> setting_registrar_;
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRequestContextImpl);
};

View File

@@ -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<CefSettingObserver> 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> registrar_;
CefRefPtr<CefSettingObserver> 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<CefRegistration> Registrar::AddObserver(
CefRefPtr<CefSettingObserver> 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<cef_content_setting_types_t>(content_type_set.GetType());
for (Registration& registration : observers_) {
registration.RunCallback(requesting_url, top_level_url, content_type);
}
}
} // namespace setting_helper

View File

@@ -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<CefRegistration> AddObserver(
CefRefPtr<CefSettingObserver> 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<HostContentSettingsMap, AcrossTasksDanglingUntriaged> settings_ =
nullptr;
base::ObserverList<Registration> observers_;
};
} // namespace setting_helper
#endif // CEF_LIBCEF_BROWSER_SETTING_HELPER_H_