blob: 7e63144418d8c5dd443f8f0e2f53acdbeed17833 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <array>
#include <memory>
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/preference/preference_api.h"
#include "chrome/browser/extensions/extension_prefs_unittest.h"
#include "chrome/test/base/testing_profile.h"
#include "components/pref_registry/pref_registry_syncable.h"
#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/buildflags/buildflags.h"
#include "extensions/common/api/types.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
#include "testing/gtest/include/gtest/gtest.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
using base::Value;
using extensions::api::types::ChromeSettingScope;
namespace extensions {
namespace {
const char kPref1[] = "path1.subpath";
const char kPref2[] = "path2";
const char kPref3[] = "path3";
const char kPref4[] = "path4";
// Default values in case an extension pref value is not overridden.
const char kDefaultPref1[] = "default pref 1";
const char kDefaultPref2[] = "default pref 2";
const char kDefaultPref3[] = "default pref 3";
const char kDefaultPref4[] = "default pref 4";
} // namespace
class ExtensionControlledPrefsTest : public PrefsPrepopulatedTestBase {
public:
ExtensionControlledPrefsTest();
~ExtensionControlledPrefsTest() override;
void RegisterPreferences(user_prefs::PrefRegistrySyncable* registry) override;
void InstallExtensionControlledPref(Extension* extension,
const std::string& key,
base::Value value);
void InstallExtensionControlledPrefIncognito(Extension* extension,
const std::string& key,
base::Value value);
void InstallExtensionControlledPrefIncognitoSessionOnly(
Extension* extension,
const std::string& key,
base::Value value);
void InstallExtension(Extension* extension);
void UninstallExtension(const ExtensionId& extension_id);
scoped_refptr<ContentSettingsStore> content_settings_store() {
return content_settings_->content_settings_store();
}
protected:
void EnsureExtensionInstalled(Extension* extension);
void EnsureExtensionUninstalled(const ExtensionId& extension_id);
TestingProfile profile_;
raw_ptr<ContentSettingsService> content_settings_;
ExtensionPrefsHelper prefs_helper_;
};
ExtensionControlledPrefsTest::ExtensionControlledPrefsTest()
: content_settings_(ContentSettingsService::Get(&profile_)),
prefs_helper_(prefs_.prefs(), prefs_.extension_pref_value_map()) {
content_settings_->OnExtensionPrefsAvailable(prefs_.prefs());
}
ExtensionControlledPrefsTest::~ExtensionControlledPrefsTest() = default;
void ExtensionControlledPrefsTest::RegisterPreferences(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterStringPref(kPref1, kDefaultPref1);
registry->RegisterStringPref(kPref2, kDefaultPref2);
registry->RegisterStringPref(kPref3, kDefaultPref3);
registry->RegisterStringPref(kPref4, kDefaultPref4);
}
void ExtensionControlledPrefsTest::InstallExtensionControlledPref(
Extension* extension,
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, ChromeSettingScope::kRegular, std::move(value));
}
void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito(
Extension* extension,
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, ChromeSettingScope::kIncognitoPersistent,
std::move(value));
}
void ExtensionControlledPrefsTest::
InstallExtensionControlledPrefIncognitoSessionOnly(Extension* extension,
const std::string& key,
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
extension->id(), key, ChromeSettingScope::kIncognitoSessionOnly,
std::move(value));
}
void ExtensionControlledPrefsTest::InstallExtension(Extension* extension) {
EnsureExtensionInstalled(extension);
}
void ExtensionControlledPrefsTest::UninstallExtension(
const ExtensionId& extension_id) {
EnsureExtensionUninstalled(extension_id);
}
void ExtensionControlledPrefsTest::EnsureExtensionInstalled(
Extension* extension) {
// Install extension the first time a preference is set for it.
auto extensions = std::to_array<Extension*>({
extension1(),
extension2(),
extension3(),
extension4(),
internal_extension(),
});
for (size_t i = 0; i < kNumInstalledExtensions; ++i) {
if (extension == extensions[i] && !installed_[i]) {
prefs()->OnExtensionInstalled(extension,
/*disable_reasons=*/{},
syncer::StringOrdinal(), std::string());
prefs()->SetIsIncognitoEnabled(extension->id(), true);
installed_[i] = true;
break;
}
}
}
void ExtensionControlledPrefsTest::EnsureExtensionUninstalled(
const ExtensionId& extension_id) {
auto extensions = std::to_array<Extension*>({
extension1(),
extension2(),
extension3(),
extension4(),
internal_extension(),
});
for (size_t i = 0; i < kNumInstalledExtensions; ++i) {
if (extensions[i]->id() == extension_id) {
installed_[i] = false;
break;
}
}
prefs()->OnExtensionUninstalled(extension_id,
mojom::ManifestLocation::kInternal, false);
}
class ControlledPrefsInstallOneExtension
: public ExtensionControlledPrefsTest {
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
}
void Verify() override {
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
}
};
TEST_F(ControlledPrefsInstallOneExtension,
ControlledPrefsInstallOneExtension) { }
// Check that we do not forget persistent incognito values after a reload.
class ControlledPrefsInstallIncognitoPersistent
: public ExtensionControlledPrefsTest {
public:
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
InstallExtensionControlledPrefIncognito(extension1(), kPref1,
base::Value("val2"));
std::unique_ptr<PrefService> incog_prefs =
prefs_.CreateIncognitoPrefService();
std::string actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
void Verify() override {
// Main pref service shall see only non-incognito settings.
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
// Incognito pref service shall see incognito values.
std::unique_ptr<PrefService> incog_prefs =
prefs_.CreateIncognitoPrefService();
actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
};
TEST_F(ControlledPrefsInstallIncognitoPersistent,
ControlledPrefsInstallIncognitoPersistent) { }
// Check that we forget 'session only' incognito values after a reload.
class ControlledPrefsInstallIncognitoSessionOnly
: public ExtensionControlledPrefsTest {
public:
ControlledPrefsInstallIncognitoSessionOnly() : iteration_(0) {}
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
InstallExtensionControlledPrefIncognitoSessionOnly(extension1(), kPref1,
base::Value("val2"));
std::unique_ptr<PrefService> incog_prefs =
prefs_.CreateIncognitoPrefService();
std::string actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
void Verify() override {
// Main pref service shall see only non-incognito settings.
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
// Incognito pref service shall see session-only incognito values only
// during first run. Once the pref service was reloaded, all values shall be
// discarded.
std::unique_ptr<PrefService> incog_prefs =
prefs_.CreateIncognitoPrefService();
actual = incog_prefs->GetString(kPref1);
if (iteration_ == 0) {
EXPECT_EQ("val2", actual);
} else {
EXPECT_EQ("val1", actual);
}
++iteration_;
}
int iteration_;
};
TEST_F(ControlledPrefsInstallIncognitoSessionOnly,
ControlledPrefsInstallIncognitoSessionOnly) { }
class ControlledPrefsUninstallExtension : public ExtensionControlledPrefsTest {
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
InstallExtensionControlledPref(extension1(), kPref2, base::Value("val2"));
scoped_refptr<ContentSettingsStore> store = content_settings_store();
ContentSettingsPattern pattern =
ContentSettingsPattern::FromString("http://[*.]example.com");
store->SetExtensionContentSetting(
extension1()->id(), pattern, pattern, ContentSettingsType::IMAGES,
CONTENT_SETTING_BLOCK, ChromeSettingScope::kRegular);
UninstallExtension(extension1()->id());
}
void Verify() override {
EXPECT_FALSE(prefs()->HasPrefForExtension(extension1()->id()));
std::string actual;
actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ(kDefaultPref1, actual);
actual = prefs()->pref_service()->GetString(kPref2);
EXPECT_EQ(kDefaultPref2, actual);
}
};
TEST_F(ControlledPrefsUninstallExtension,
ControlledPrefsUninstallExtension) { }
// Tests triggering of notifications to registered observers.
class ControlledPrefsNotifyWhenNeeded : public ExtensionControlledPrefsTest {
void Initialize() override {
using testing::_;
using testing::Mock;
using testing::StrEq;
MockPrefChangeCallback observer(prefs()->pref_service());
PrefChangeRegistrar registrar;
registrar.Init(prefs()->pref_service());
registrar.Add(kPref1, observer.GetCallback());
MockPrefChangeCallback incognito_observer(prefs()->pref_service());
std::unique_ptr<PrefService> incog_prefs =
prefs_.CreateIncognitoPrefService();
PrefChangeRegistrar incognito_registrar;
incognito_registrar.Init(incog_prefs.get());
incognito_registrar.Add(kPref1, incognito_observer.GetCallback());
// Write value and check notification.
EXPECT_CALL(observer, OnPreferenceChanged(_));
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_));
InstallExtensionControlledPref(extension1(), kPref1,
base::Value("https://www.chromium.org"));
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
// Write same value.
EXPECT_CALL(observer, OnPreferenceChanged(_)).Times(0);
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_)).Times(0);
InstallExtensionControlledPref(extension1(), kPref1,
base::Value("https://www.chromium.org"));
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
// Change value.
EXPECT_CALL(observer, OnPreferenceChanged(_));
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_));
InstallExtensionControlledPref(extension1(), kPref1,
base::Value("chrome://newtab"));
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
// Change only incognito persistent value.
EXPECT_CALL(observer, OnPreferenceChanged(_)).Times(0);
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_));
InstallExtensionControlledPrefIncognito(extension1(), kPref1,
base::Value("chrome://newtab2"));
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
// Change only incognito session-only value.
EXPECT_CALL(observer, OnPreferenceChanged(_)).Times(0);
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_));
InstallExtensionControlledPrefIncognito(extension1(), kPref1,
base::Value("chrome://newtab3"));
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
// Uninstall.
EXPECT_CALL(observer, OnPreferenceChanged(_));
EXPECT_CALL(incognito_observer, OnPreferenceChanged(_));
UninstallExtension(extension1()->id());
Mock::VerifyAndClearExpectations(&observer);
Mock::VerifyAndClearExpectations(&incognito_observer);
registrar.Remove(kPref1);
incognito_registrar.Remove(kPref1);
}
void Verify() override {
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ(kDefaultPref1, actual);
}
};
TEST_F(ControlledPrefsNotifyWhenNeeded,
ControlledPrefsNotifyWhenNeeded) { }
// Tests disabling an extension.
class ControlledPrefsDisableExtension : public ExtensionControlledPrefsTest {
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
prefs()->AddDisableReason(extension1()->id(),
disable_reason::DISABLE_USER_ACTION);
}
void Verify() override {
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ(kDefaultPref1, actual);
}
};
TEST_F(ControlledPrefsDisableExtension, ControlledPrefsDisableExtension) { }
// Tests disabling and reenabling an extension.
class ControlledPrefsReenableExtension : public ExtensionControlledPrefsTest {
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1, base::Value("val1"));
prefs()->AddDisableReason(extension1()->id(),
disable_reason::DISABLE_USER_ACTION);
prefs()->ClearDisableReasons(extension1()->id());
}
void Verify() override {
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
}
};
TEST_F(ControlledPrefsDisableExtension, ControlledPrefsReenableExtension) { }
class ControlledPrefsSetExtensionControlledPref
: public ExtensionControlledPrefsTest {
public:
void Initialize() override {
InstallExtensionControlledPref(extension1(), kPref1,
base::Value("https://www.chromium.org"));
InstallExtensionControlledPrefIncognito(
extension1(), kPref1, base::Value("https://www.chromium.org"));
prefs_.RecreateExtensionPrefs();
}
void Verify() override {}
};
TEST_F(ControlledPrefsSetExtensionControlledPref,
ControlledPrefsSetExtensionControlledPref) { }
// Tests that the switches::kDisableExtensions command-line flag prevents
// extension controlled preferences from being enacted.
class ControlledPrefsDisableExtensions : public ExtensionControlledPrefsTest {
public:
ControlledPrefsDisableExtensions() = default;
~ControlledPrefsDisableExtensions() override = default;
void Initialize() override {
InstallExtensionControlledPref(internal_extension(), kPref1,
base::Value("internal extension value"));
EXPECT_TRUE(Manifest::IsExternalLocation(extension1()->location()));
InstallExtensionControlledPref(extension1(), kPref2,
base::Value("external extension value"));
// This becomes only active in the second verification phase.
prefs_.set_extensions_disabled(true);
}
void Verify() override {
// Internal extensions are not loaded with --disable-extensions. This means
// that the preference will be reset on the second verification run (when
// the ExtensionPrefs are recreated).
std::string pref1_actual = prefs()->pref_service()->GetString(kPref1);
if (iteration_ == 0) {
EXPECT_EQ("internal extension value", pref1_actual);
++iteration_;
} else {
EXPECT_EQ(kDefaultPref1, pref1_actual);
}
// External extensions are loaded even when extensions are disabled (though
// they likely shouldn't be, see https://crbug.com/833540). Because of this,
// the preference should still be controlled by the external extension.
// Regression test for https://crbug.com/828295.
std::string pref2_actual = prefs()->pref_service()->GetString(kPref2);
EXPECT_EQ("external extension value", pref2_actual);
}
private:
int iteration_ = 0;
};
TEST_F(ControlledPrefsDisableExtensions, ControlledPrefsDisableExtensions) { }
} // namespace extensions