0

[Extensions] Move some PreferenceAPI functionality to a helper class.

This CL begins the process of disentagling the Preference API,
the FontSettings API, and the Settings Overrides API. It moves some
base functionality for extension prefs into a helper class in
/extensions/browser.

The existing PreferenceAPI class uses an instance of that helper to implement the APIs from its former base class, PreferenceAPIBase. This avoids pushing the code changes outside of this area, at the cost of some temporary code.

Bug: 883570
Change-Id: I866e2fb63772319d3563c7053712341bcc442533
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3894954
Reviewed-by: Emilia Paz <emiliapaz@chromium.org>
Commit-Queue: David Bertoni <dbertoni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1047286}
This commit is contained in:
David Bertoni
2022-09-15 03:12:33 +00:00
committed by Chromium LUCI CQ
parent 4063c00422
commit f45d7abd3f
6 changed files with 219 additions and 178 deletions

@ -683,83 +683,11 @@ void PreferenceEventRouter::ObserveOffTheRecordPrefs(PrefService* prefs) {
}
}
void PreferenceAPIBase::SetExtensionControlledPref(
const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope,
base::Value value) {
#ifndef NDEBUG
const PrefService::Preference* pref =
extension_prefs()->pref_service()->FindPreference(pref_key);
DCHECK(pref) << "Extension controlled preference key " << pref_key
<< " not registered.";
DCHECK_EQ(pref->GetType(), value.type())
<< "Extension controlled preference " << pref_key << " has wrong type.";
#endif
std::string scope_string;
// ScopeToPrefName() returns false if the scope is not persisted.
if (pref_names::ScopeToPrefName(scope, &scope_string)) {
// Also store in persisted Preferences file to recover after a
// browser restart.
ExtensionPrefs::ScopedDictionaryUpdate update(extension_prefs(),
extension_id,
scope_string);
auto preference = update.Create();
preference->SetWithoutPathExpansion(
pref_key, base::Value::ToUniquePtrValue(value.Clone()));
}
extension_pref_value_map()->SetExtensionPref(extension_id, pref_key, scope,
std::move(value));
}
void PreferenceAPIBase::RemoveExtensionControlledPref(
const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope) {
DCHECK(extension_prefs()->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
std::string scope_string;
if (pref_names::ScopeToPrefName(scope, &scope_string)) {
ExtensionPrefs::ScopedDictionaryUpdate update(extension_prefs(),
extension_id,
scope_string);
auto preference = update.Get();
if (preference)
preference->RemoveWithoutPathExpansion(pref_key, nullptr);
}
extension_pref_value_map()->RemoveExtensionPref(
extension_id, pref_key, scope);
}
bool PreferenceAPIBase::CanExtensionControlPref(
const std::string& extension_id,
const std::string& pref_key,
bool incognito) {
DCHECK(extension_prefs()->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
return extension_pref_value_map()->CanExtensionControlPref(
extension_id, pref_key, incognito);
}
bool PreferenceAPIBase::DoesExtensionControlPref(
const std::string& extension_id,
const std::string& pref_key,
bool* from_incognito) {
DCHECK(extension_prefs()->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
return extension_pref_value_map()->DoesExtensionControlPref(
extension_id, pref_key, from_incognito);
}
PreferenceAPI::PreferenceAPI(content::BrowserContext* context)
: profile_(Profile::FromBrowserContext(context)) {
: profile_(Profile::FromBrowserContext(context)),
prefs_helper_(
ExtensionPrefs::Get(profile_),
ExtensionPrefValueMapFactory::GetForBrowserContext(profile_)) {
for (const auto& pref : kPrefMapping) {
std::string event_name;
APIPermissionID permission = APIPermissionID::kInvalid;
@ -782,7 +710,7 @@ PreferenceAPI::~PreferenceAPI() = default;
void PreferenceAPI::Shutdown() {
EventRouter::Get(profile_)->UnregisterObserver(this);
if (!extension_prefs()->extensions_disabled())
if (!prefs_helper_.prefs()->extensions_disabled())
ClearIncognitoSessionOnlyContentSettings();
content_settings_store()->RemoveObserver(this);
}
@ -816,13 +744,13 @@ void PreferenceAPI::EnsurePreferenceEventRouterCreated() {
void PreferenceAPI::OnContentSettingChanged(const std::string& extension_id,
bool incognito) {
if (incognito) {
extension_prefs()->UpdateExtensionPref(
prefs_helper_.prefs()->UpdateExtensionPref(
extension_id, pref_names::kPrefIncognitoContentSettings,
base::Value::ToUniquePtrValue(
base::Value(content_settings_store()->GetSettingsForExtension(
extension_id, kExtensionPrefsScopeIncognitoPersistent))));
} else {
extension_prefs()->UpdateExtensionPref(
prefs_helper_.prefs()->UpdateExtensionPref(
extension_id, pref_names::kPrefContentSettings,
base::Value::ToUniquePtrValue(
base::Value(content_settings_store()->GetSettingsForExtension(
@ -832,21 +760,13 @@ void PreferenceAPI::OnContentSettingChanged(const std::string& extension_id,
void PreferenceAPI::ClearIncognitoSessionOnlyContentSettings() {
ExtensionIdList extension_ids;
extension_prefs()->GetExtensions(&extension_ids);
prefs_helper_.prefs()->GetExtensions(&extension_ids);
for (const auto& id : extension_ids) {
content_settings_store()->ClearContentSettingsForExtension(
id, kExtensionPrefsScopeIncognitoSessionOnly);
}
}
ExtensionPrefs* PreferenceAPI::extension_prefs() {
return ExtensionPrefs::Get(profile_);
}
ExtensionPrefValueMap* PreferenceAPI::extension_pref_value_map() {
return ExtensionPrefValueMapFactory::GetForBrowserContext(profile_);
}
scoped_refptr<ContentSettingsStore> PreferenceAPI::content_settings_store() {
return ContentSettingsService::Get(profile_)->content_settings_store();
}

@ -18,7 +18,7 @@
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_prefs_scope.h"
#include "extensions/browser/extension_prefs_helper.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/crosapi/mojom/prefs.mojom-shared.h"
@ -29,7 +29,6 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#endif
class ExtensionPrefValueMap;
class PrefService;
namespace base {
@ -37,7 +36,6 @@ class Value;
}
namespace extensions {
class ExtensionPrefs;
class PreferenceEventRouter : public ProfileObserver {
public:
@ -95,53 +93,7 @@ class PreferenceEventRouter : public ProfileObserver {
#endif
};
// The class containing the implementation for extension-controlled preference
// manipulation. This implementation is separate from PreferenceAPI, since
// we need to be able to use these methods in testing, where we use
// TestExtensionPrefs and don't construct a profile.
//
// See also PreferenceAPI and TestPreferenceAPI.
class PreferenceAPIBase {
public:
// Functions for manipulating preference values that are controlled by the
// extension. In other words, these are not pref values *about* the extension,
// but rather about something global the extension wants to override.
// Set a new extension-controlled preference value.
void SetExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope,
base::Value value);
// Remove an extension-controlled preference value.
void RemoveExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope);
// Returns true if currently no extension with higher precedence controls the
// preference.
bool CanExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool incognito);
// Returns true if extension |extension_id| currently controls the
// preference. If |from_incognito| is not NULL, looks at incognito preferences
// first, and |from_incognito| is set to true if the effective pref value is
// coming from the incognito preferences, false if it is coming from the
// normal ones.
bool DoesExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool* from_incognito);
protected:
// Virtual for testing.
virtual ExtensionPrefs* extension_prefs() = 0;
virtual ExtensionPrefValueMap* extension_pref_value_map() = 0;
virtual scoped_refptr<ContentSettingsStore> content_settings_store() = 0;
};
class PreferenceAPI : public PreferenceAPIBase,
public BrowserContextKeyedAPI,
class PreferenceAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer,
public ContentSettingsStore::Observer {
public:
@ -167,6 +119,43 @@ class PreferenceAPI : public PreferenceAPIBase,
// Ensures that a PreferenceEventRouter is created only once.
void EnsurePreferenceEventRouterCreated();
// Set a new extension-controlled preference value.
void SetExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope,
base::Value value) {
prefs_helper_.SetExtensionControlledPref(extension_id, pref_key, scope,
std::move(value));
}
// Remove an extension-controlled preference value.
void RemoveExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope) {
prefs_helper_.RemoveExtensionControlledPref(extension_id, pref_key, scope);
}
// Returns true if currently no extension with higher precedence controls the
// preference.
bool CanExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool incognito) {
return prefs_helper_.CanExtensionControlPref(extension_id, pref_key,
incognito);
}
// Returns true if extension |extension_id| currently controls the
// preference. If `from_incognito` is not NULL, looks at incognito preferences
// first, and `from_incognito` is set to true if the effective pref value is
// coming from the incognito preferences, false if it is coming from the
// normal ones.
bool DoesExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool* from_incognito) {
return prefs_helper_.DoesExtensionControlPref(extension_id, pref_key,
from_incognito);
}
private:
friend class BrowserContextKeyedAPIFactory<PreferenceAPI>;
@ -177,12 +166,10 @@ class PreferenceAPI : public PreferenceAPIBase,
// Clears incognito session-only content settings for all extensions.
void ClearIncognitoSessionOnlyContentSettings();
// PreferenceAPIBase implementation.
ExtensionPrefs* extension_prefs() override;
ExtensionPrefValueMap* extension_pref_value_map() override;
scoped_refptr<ContentSettingsStore> content_settings_store() override;
scoped_refptr<ContentSettingsStore> content_settings_store();
raw_ptr<Profile> profile_;
ExtensionPrefsHelper prefs_helper_;
// BrowserContextKeyedAPI implementation.
static const char* service_name() {

@ -16,6 +16,7 @@
#include "components/prefs/mock_pref_change_callback.h"
#include "extensions/browser/api/content_settings/content_settings_service.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_prefs_helper.h"
#include "extensions/common/extension.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -38,37 +39,6 @@ const char kDefaultPref4[] = "default pref 4";
} // namespace
// An implementation of the PreferenceAPI which returns the ExtensionPrefs and
// ExtensionPrefValueMap from the TestExtensionPrefs, rather than from a
// profile (which we don't create in unittests).
class TestPreferenceAPI : public PreferenceAPIBase {
public:
explicit TestPreferenceAPI(TestExtensionPrefs* test_extension_prefs,
ContentSettingsService* content_settings)
: test_extension_prefs_(test_extension_prefs),
content_settings_(content_settings) {}
TestPreferenceAPI(const TestPreferenceAPI&) = delete;
TestPreferenceAPI& operator=(const TestPreferenceAPI&) = delete;
~TestPreferenceAPI() {}
private:
// PreferenceAPIBase implementation.
ExtensionPrefs* extension_prefs() override {
return test_extension_prefs_->prefs();
}
ExtensionPrefValueMap* extension_pref_value_map() override {
return test_extension_prefs_->extension_pref_value_map();
}
scoped_refptr<ContentSettingsStore> content_settings_store() override {
return content_settings_->content_settings_store();
}
raw_ptr<TestExtensionPrefs> test_extension_prefs_;
raw_ptr<ContentSettingsService> content_settings_;
};
class ExtensionControlledPrefsTest : public PrefsPrepopulatedTestBase {
public:
ExtensionControlledPrefsTest();
@ -98,13 +68,13 @@ class ExtensionControlledPrefsTest : public PrefsPrepopulatedTestBase {
TestingProfile profile_;
raw_ptr<ContentSettingsService> content_settings_;
TestPreferenceAPI test_preference_api_;
ExtensionPrefsHelper prefs_helper_;
};
ExtensionControlledPrefsTest::ExtensionControlledPrefsTest()
: PrefsPrepopulatedTestBase(),
content_settings_(ContentSettingsService::Get(&profile_)),
test_preference_api_(&prefs_, content_settings_) {
prefs_helper_(prefs_.prefs(), prefs_.extension_pref_value_map()) {
content_settings_->OnExtensionPrefsAvailable(prefs_.prefs());
}
@ -124,7 +94,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPref(
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
test_preference_api_.SetExtensionControlledPref(
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, kExtensionPrefsScopeRegular, std::move(value));
}
@ -133,7 +103,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito(
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
test_preference_api_.SetExtensionControlledPref(
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, kExtensionPrefsScopeIncognitoPersistent,
std::move(value));
}
@ -143,7 +113,7 @@ void ExtensionControlledPrefsTest::
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
test_preference_api_.SetExtensionControlledPref(
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, kExtensionPrefsScopeIncognitoSessionOnly,
std::move(value));
}

@ -332,6 +332,8 @@ source_set("browser_sources") {
"extension_prefs.h",
"extension_prefs_factory.cc",
"extension_prefs_factory.h",
"extension_prefs_helper.cc",
"extension_prefs_helper.h",
"extension_prefs_observer.h",
"extension_prefs_scope.h",
"extension_protocols.cc",

@ -0,0 +1,91 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_prefs_helper.h"
#include "build/build_config.h"
#include "components/prefs/pref_service.h"
#include "extensions/browser/extension_pref_value_map.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/pref_names.h"
namespace extensions {
ExtensionPrefsHelper::ExtensionPrefsHelper(ExtensionPrefs* prefs,
ExtensionPrefValueMap* value_map)
: prefs_(prefs), value_map_(value_map) {}
ExtensionPrefsHelper::~ExtensionPrefsHelper() = default;
void ExtensionPrefsHelper::SetExtensionControlledPref(
const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope,
base::Value value) {
#ifndef NDEBUG
const PrefService::Preference* pref =
prefs_->pref_service()->FindPreference(pref_key);
DCHECK(pref) << "Extension controlled preference key " << pref_key
<< " not registered.";
DCHECK_EQ(pref->GetType(), value.type())
<< "Extension controlled preference " << pref_key << " has wrong type.";
#endif
std::string scope_string;
// ScopeToPrefName() returns false if the scope is not persisted.
if (pref_names::ScopeToPrefName(scope, &scope_string)) {
// Also store in persisted Preferences file to recover after a
// browser restart.
ExtensionPrefs::ScopedDictionaryUpdate update(prefs_, extension_id,
scope_string);
auto preference = update.Create();
preference->SetWithoutPathExpansion(
pref_key, base::Value::ToUniquePtrValue(value.Clone()));
}
value_map_->SetExtensionPref(extension_id, pref_key, scope, std::move(value));
}
void ExtensionPrefsHelper::RemoveExtensionControlledPref(
const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope) {
DCHECK(prefs_->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
std::string scope_string;
if (pref_names::ScopeToPrefName(scope, &scope_string)) {
ExtensionPrefs::ScopedDictionaryUpdate update(prefs_, extension_id,
scope_string);
auto preference = update.Get();
if (preference)
preference->RemoveWithoutPathExpansion(pref_key, nullptr);
}
value_map_->RemoveExtensionPref(extension_id, pref_key, scope);
}
bool ExtensionPrefsHelper::CanExtensionControlPref(
const std::string& extension_id,
const std::string& pref_key,
bool incognito) {
DCHECK(prefs_->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
return value_map_->CanExtensionControlPref(extension_id, pref_key, incognito);
}
bool ExtensionPrefsHelper::DoesExtensionControlPref(
const std::string& extension_id,
const std::string& pref_key,
bool* from_incognito) {
DCHECK(prefs_->pref_service()->FindPreference(pref_key))
<< "Extension controlled preference key " << pref_key
<< " not registered.";
return value_map_->DoesExtensionControlPref(extension_id, pref_key,
from_incognito);
}
} // namespace extensions

@ -0,0 +1,71 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_EXTENSION_PREFS_HELPER_H_
#define EXTENSIONS_BROWSER_EXTENSION_PREFS_HELPER_H_
#include <string>
#include "extensions/browser/extension_prefs_scope.h"
class ExtensionPrefValueMap;
namespace base {
class Value;
}
namespace extensions {
class ExtensionPrefs;
class ExtensionPrefsHelper {
public:
ExtensionPrefsHelper(ExtensionPrefs* prefs, ExtensionPrefValueMap* value_map);
ExtensionPrefsHelper(const ExtensionPrefsHelper&) = delete;
ExtensionPrefsHelper& operator=(const ExtensionPrefsHelper&) = delete;
~ExtensionPrefsHelper();
// Functions for manipulating preference values that are controlled by the
// extension. In other words, these are not pref values *about* the extension,
// but rather about something global the extension wants to override.
// Set a new extension-controlled preference value.
void SetExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope,
base::Value value);
// Remove an extension-controlled preference value.
void RemoveExtensionControlledPref(const std::string& extension_id,
const std::string& pref_key,
ExtensionPrefsScope scope);
// Returns true if currently no extension with higher precedence controls the
// preference.
bool CanExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool incognito);
// Returns true if extension `extension_id` currently controls the
// preference. If `from_incognito` is not a nullptr value, looks at incognito
// preferences first, and |from_incognito| is set to true if the effective
// pref value is coming from the incognito preferences, false if it is coming
// from the normal ones.
bool DoesExtensionControlPref(const std::string& extension_id,
const std::string& pref_key,
bool* from_incognito);
ExtensionPrefs* prefs() { return prefs_; }
const ExtensionPrefs* prefs() const { return prefs_; }
private:
ExtensionPrefs* const prefs_;
ExtensionPrefValueMap* const value_map_;
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_EXTENSION_PREFS_HELPER_H_