Add a feature that allows control over DSE permission logic
(cherry picked from commit 7c6065feffecc3320c70207f18a556f0bc69b4d0)
(cherry picked from commit f08fe1a322e11f26282994e040ac696813747717)
Bug: 1228658,1238034
Change-Id: I46c0050f726e6c5472a1b9bc4d75f2cc38b5e0c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3021149
Commit-Queue: Andy Paicu <andypaicu@chromium.org>
Reviewed-by: Balazs Engedy <engedy@chromium.org>
Reviewed-by: Martin Šrámek <msramek@chromium.org>
Cr-Original-Original-Commit-Position: refs/heads/master@{#903402}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3052591
Reviewed-by: Andy Paicu <andypaicu@chromium.org>
Reviewed-by: Illia Klimov <elklm@google.com>
Auto-Submit: Andy Paicu <andypaicu@chromium.org>
Commit-Queue: Illia Klimov <elklm@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Original-Commit-Position: refs/branch-heads/4577@{#148}
Cr-Original-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3081944
Cr-Commit-Position: refs/branch-heads/4515@{#2007}
Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287}
diff --git a/chrome/browser/android/search_permissions/search_permissions_service.cc b/chrome/browser/android/search_permissions/search_permissions_service.cc
index 33f41930..51f5581d 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service.cc
+++ b/chrome/browser/android/search_permissions/search_permissions_service.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/values.h"
#include "chrome/browser/android/search_permissions/search_geolocation_disclosure_tab_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -20,6 +21,7 @@
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/permissions/features.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
@@ -247,7 +249,8 @@
ContentSetting SearchPermissionsService::RestoreOldSettingAndReturnPrevious(
const GURL& dse_origin,
ContentSettingsType type,
- ContentSetting setting_to_restore) {
+ ContentSetting setting_to_restore,
+ bool preserve_block_setting) {
// Read the current value of the old DSE. This is the DSE setting that we want
// to try to apply to the new DSE origin.
ContentSetting dse_setting = GetContentSetting(dse_origin, type);
@@ -261,6 +264,10 @@
dse_setting = CONTENT_SETTING_BLOCK;
}
+ // If `preserve_block_setting` is set we don't restore a "BLOCK" setting.
+ if (dse_setting == CONTENT_SETTING_BLOCK && preserve_block_setting)
+ setting_to_restore = CONTENT_SETTING_BLOCK;
+
// Restore the setting for the old origin. If the user has changed the setting
// since the origin became the DSE, we reset the setting so the user will be
// prompted.
@@ -282,7 +289,8 @@
->RemoveEmbargoAndResetCounts(new_dse_origin, type);
ContentSetting dse_setting = RestoreOldSettingAndReturnPrevious(
- old_dse_origin, type, old_dse_setting_to_restore);
+ old_dse_origin, type, old_dse_setting_to_restore,
+ false /* preserve_block_setting */);
ContentSetting new_dse_setting_to_restore =
GetContentSetting(new_dse_origin, type);
@@ -313,21 +321,26 @@
void SearchPermissionsService::InitializeSettingsIfNeeded() {
GURL dse_origin = delegate_->GetDSEOrigin().GetURL();
- // This can happen in tests or if the DSE is disabled by policy. If that's
- // the case, we restore the old settings and erase the pref.
- if (!dse_origin.is_valid()) {
+ // `dse_origin` can be invalid in tests or if the DSE is disabled by policy.
+ // If that's the case or if `RevertDSEAutomaticPermissions` is enabled, we
+ // restore the old settings and erase the pref.
+ const bool disabled_by_policy = !dse_origin.is_valid();
+ if (disabled_by_policy ||
+ base::FeatureList::IsEnabled(
+ permissions::features::kRevertDSEAutomaticPermissions)) {
if (pref_service_->HasPrefPath(prefs::kDSEPermissionsSettings)) {
- pref_service_->SetBoolean(prefs::kDSEWasDisabledByPolicy, true);
+ if (disabled_by_policy)
+ pref_service_->SetBoolean(prefs::kDSEWasDisabledByPolicy, true);
PrefValue pref = GetDSEPref();
GURL old_dse_origin(pref.dse_origin);
- RestoreOldSettingAndReturnPrevious(old_dse_origin,
- ContentSettingsType::GEOLOCATION,
- pref.geolocation_setting_to_restore);
+ RestoreOldSettingAndReturnPrevious(
+ old_dse_origin, ContentSettingsType::GEOLOCATION,
+ pref.geolocation_setting_to_restore, !disabled_by_policy);
if (pref.notifications_setting_to_restore != CONTENT_SETTING_DEFAULT) {
RestoreOldSettingAndReturnPrevious(
old_dse_origin, ContentSettingsType::NOTIFICATIONS,
- pref.notifications_setting_to_restore);
+ pref.notifications_setting_to_restore, !disabled_by_policy);
}
pref_service_->ClearPref(prefs::kDSEPermissionsSettings);
}
diff --git a/chrome/browser/android/search_permissions/search_permissions_service.h b/chrome/browser/android/search_permissions/search_permissions_service.h
index 3df7b2c..91d0f39 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service.h
+++ b/chrome/browser/android/search_permissions/search_permissions_service.h
@@ -120,7 +120,8 @@
ContentSetting RestoreOldSettingAndReturnPrevious(
const GURL& dse_origin,
ContentSettingsType type,
- ContentSetting setting_to_restore);
+ ContentSetting setting_to_restore,
+ bool preserve_block_setting);
// Helper function for OnDSEChanged which transitions the DSE setting for a
// specific permission. It returns the content setting to be restored later
diff --git a/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc b/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
index 2d6338a..99f0112 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
+++ b/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
@@ -9,13 +9,17 @@
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "chrome/browser/android/search_permissions/search_geolocation_disclosure_tab_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/pref_names.h"
+#include "components/permissions/features.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/permissions/permission_result.h"
#include "components/prefs/pref_service.h"
@@ -77,7 +81,9 @@
void SetUp() override {
profile_ = std::make_unique<TestingProfile>();
- ClearNotificationsChannels();
+ // Because notification channel settings aren't tied to the profile, they
+ // will persist across tests. We need to make sure they're clean here.
+ ClearContentSettings(ContentSettingsType::NOTIFICATIONS);
auto test_delegate = std::make_unique<TestSearchEngineDelegate>();
test_delegate_ = test_delegate.get();
@@ -88,22 +94,18 @@
void TearDown() override {
test_delegate_ = nullptr;
- ClearNotificationsChannels();
+ // Because notification channel settings aren't tied to the profile, they
+ // will persist across tests. We need to make sure they're reset here.
+ ClearContentSettings(ContentSettingsType::NOTIFICATIONS);
profile_.reset();
}
- void ClearNotificationsChannels() {
- // Because notification channel settings aren't tied to the profile, they
- // will persist across tests. We need to make sure they're reset here.
- SetContentSetting(kGoogleURL, ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_DEFAULT);
- SetContentSetting(kGoogleAusURL, ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_DEFAULT);
- SetContentSetting(kGoogleHTTPURL, ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_DEFAULT);
- SetContentSetting(kExampleURL, ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_DEFAULT);
+ void ClearContentSettings(ContentSettingsType type) {
+ SetContentSetting(kGoogleURL, type, CONTENT_SETTING_DEFAULT);
+ SetContentSetting(kGoogleAusURL, type, CONTENT_SETTING_DEFAULT);
+ SetContentSetting(kGoogleHTTPURL, type, CONTENT_SETTING_DEFAULT);
+ SetContentSetting(kExampleURL, type, CONTENT_SETTING_DEFAULT);
}
TestingProfile* profile() { return profile_.get(); }
@@ -637,3 +639,352 @@
EXPECT_EQ(CONTENT_SETTING_BLOCK,
GetContentSetting(kGoogleURL, ContentSettingsType::GEOLOCATION));
}
+
+// Setting the `RevertDSEAutomaticPermissions` feature disables DSE permissions.
+TEST_F(SearchPermissionsServiceTest, DSEPermissionsCanBeDisabledByFeature) {
+ constexpr struct {
+ ContentSetting initial_setting;
+ ContentSetting expected_setting_after_autogrant;
+ ContentSetting expected_setting_after_autogrant_reverted;
+ } kTests[] = {
+ {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+ {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+ {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+ };
+
+ test_delegate()->ChangeDSEOrigin(kGoogleURL);
+
+ for (const auto& test : kTests) {
+ for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+ ContentSettingsType::GEOLOCATION}) {
+ // Notifications can not be set to ASK.
+ if (test.initial_setting == CONTENT_SETTING_ASK &&
+ type == ContentSettingsType::NOTIFICATIONS) {
+ continue;
+ }
+
+ ClearContentSettings(type);
+ SetContentSetting(kGoogleURL, type, test.initial_setting);
+
+ // Initialize DSE and verify the expected setting.
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant,
+ GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+ // the SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant_reverted,
+ GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant,
+ GetContentSetting(kGoogleURL, type));
+ }
+ }
+}
+
+// Change the permission state for the DSE origin while DSE is active. Also test
+// how the `RevertDSEAutomaticPermissions` feature interacts with this scenario.
+TEST_F(SearchPermissionsServiceTest,
+ DSEOriginPermissionsChangeBeforeFeatureIsEnabled) {
+ constexpr struct {
+ ContentSetting initial_setting;
+ ContentSetting expected_setting_after_autogrant;
+ ContentSetting updated_setting;
+ ContentSetting expected_setting_after_update;
+ ContentSetting expected_setting_after_autogrant_reverted;
+ ContentSetting expected_setting_after_autogrant_reverted_disabled;
+ } kTests[] = {
+ {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+ // Critical journey: if the user decided to change the DSE origin's
+ // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+ {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+ // Critical journey: if the user decided to change the DSE origin's
+ // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+ {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW,
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+ {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK,
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+ // Critical journey: if the user decided to change the DSE origin's
+ // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+ {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ };
+
+ for (const auto& test : kTests) {
+ for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+ ContentSettingsType::GEOLOCATION}) {
+ // Notifications can not be set to ASK on Android as notification channels
+ // explicitly rely on the state being only BLOCK/ALLOW/DEFAULT.
+ if (test.initial_setting == CONTENT_SETTING_ASK &&
+ type == ContentSettingsType::NOTIFICATIONS) {
+ continue;
+ }
+
+ ClearContentSettings(type);
+ SetContentSetting(kGoogleURL, type, test.initial_setting);
+
+ // Initialize DSE and verify the expected setting.
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant,
+ GetContentSetting(kGoogleURL, type));
+
+ // Change the setting of the DSE origin and verify the result.
+ SetContentSetting(kGoogleURL, type, test.updated_setting);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_update,
+ GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+ // the SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant_reverted,
+ GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test.expected_setting_after_autogrant_reverted_disabled,
+ GetContentSetting(kGoogleURL, type));
+ }
+ }
+}
+
+// Change the permission state for the DSE origin while the
+// `RevertDSEAutomaticPermissions` feature is active. Also test how disabling
+// the feature again afterwards will affect the permission.
+TEST_F(SearchPermissionsServiceTest,
+ DSEOriginPermissionsChangeAfterFeatureIsEnabled) {
+ constexpr struct {
+ ContentSetting initial_setting;
+ ContentSetting expected_setting_after_autogrant;
+ ContentSetting expected_setting_after_autogrant_reverted;
+ } kTestsBeforeSettingUpdate[] = {
+ {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+ {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+ {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+ };
+
+ constexpr struct {
+ ContentSetting updated_setting;
+ ContentSetting expected_setting_after_update;
+ ContentSetting expected_setting_after_autogrant_reverted_disabled;
+ } kTestsAfterSettingUpdate[] = {
+ {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+ {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+ {CONTENT_SETTING_ASK, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+ {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+ };
+
+ // The test cases are split into two halves. Every combination of
+ // `kTestsBeforeSettingUpdate` and `kTestsBeforeSettingUpdate` entries
+ // produces a test case.
+ for (const auto& test_before : kTestsBeforeSettingUpdate) {
+ for (const auto& test_after : kTestsAfterSettingUpdate) {
+ for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+ ContentSettingsType::GEOLOCATION}) {
+ // Notifications can not be set to ASK on Android as notification
+ // channels explicitly rely on the state being only BLOCK/ALLOW/DEFAULT.
+ if (test_before.initial_setting == CONTENT_SETTING_ASK &&
+ type == ContentSettingsType::NOTIFICATIONS) {
+ continue;
+ }
+
+ ClearContentSettings(type);
+ SetContentSetting(kGoogleURL, type, test_before.initial_setting);
+
+ // Initialize DSE and verify the expected setting.
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(test_before.expected_setting_after_autogrant,
+ GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+ // the SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test_before.expected_setting_after_autogrant_reverted,
+ GetContentSetting(kGoogleURL, type));
+
+ // Notifications can not be set to ASK on Android as notification
+ // channels explicitly rely on the state being only
+ // BLOCK/ALLOW/DEFAULT.
+ if (test_after.updated_setting == CONTENT_SETTING_ASK &&
+ type == ContentSettingsType::NOTIFICATIONS) {
+ continue;
+ }
+
+ // Change the setting of the DSE origin and verify the result. This
+ // means that the user made a decision for the DSE origin while DSE
+ // is inactive.
+ SetContentSetting(kGoogleURL, type, test_after.updated_setting);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test_after.expected_setting_after_update,
+ GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService. Test how it handles the user's setting
+ // change while DSE was inactive.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(test_after.expected_setting_after_autogrant_reverted_disabled,
+ GetContentSetting(kGoogleURL, type));
+ }
+ }
+ }
+}
+
+// Tests the scenario in which the permission is disabled by default for all
+// origins.
+TEST_F(SearchPermissionsServiceTest, PermissionsDisabledByDefault) {
+ HostContentSettingsMap* hcsm =
+ HostContentSettingsMapFactory::GetForProfile(profile());
+
+ for (const auto type :
+ {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+ ClearContentSettings(type);
+ hcsm->SetDefaultContentSetting(type, CONTENT_SETTING_BLOCK);
+
+ // Global setting should still apply.
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by the
+ // SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+ }
+}
+
+// Tests the scenario in which the permission is disabled by default for all
+// origins after DSE has granted permission.
+TEST_F(SearchPermissionsServiceTest,
+ PermissionsDisabledByDefaultAfterAutogrant) {
+ HostContentSettingsMap* hcsm =
+ HostContentSettingsMapFactory::GetForProfile(profile());
+
+ for (const auto type :
+ {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+ ClearContentSettings(type);
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+ // Set the default content setting after the DSE has been applied.
+ hcsm->SetDefaultContentSetting(type, CONTENT_SETTING_BLOCK);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by the
+ // SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+ }
+}
+
+// Test repeatedly enabling and disabling the `RevertDSEAutomaticPermissions`
+// feature.
+TEST_F(SearchPermissionsServiceTest,
+ DSEPermissionsRepeatedlyDisabledByFeature) {
+ for (const auto type :
+ {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+ ClearContentSettings(type);
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+ int num_repetitions = 5;
+ while (num_repetitions--) {
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+ // the SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+ }
+ }
+}
+
+// Test disabling DSE via the `RevertDSEAutomaticPermissions` feature after the
+// DSE origin has changed at least once.
+TEST_F(SearchPermissionsServiceTest, DSEDisabledAfterOriginChange) {
+ for (const auto type :
+ {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+ test_delegate()->ChangeDSEOrigin(kGoogleURL);
+ ClearContentSettings(type);
+ ReinitializeService(true /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+ // Change DSE origin and make sure the setting is correctly updated.
+ test_delegate()->ChangeDSEOrigin(kGoogleAusURL);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleAusURL, type));
+ EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+
+ // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+ // the SearchPermissionsService.
+ {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ permissions::features::kRevertDSEAutomaticPermissions);
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleAusURL, type));
+ EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+ }
+
+ // If the feature is disabled again, DSE starts being controlled by the
+ // SearchPermissionsService.
+ ReinitializeService(false /* clear_pref */);
+ EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleAusURL, type));
+ EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+ }
+}
diff --git a/components/permissions/features.cc b/components/permissions/features.cc
index d48e35a..744b7645 100644
--- a/components/permissions/features.cc
+++ b/components/permissions/features.cc
@@ -50,6 +50,13 @@
"kPermissionPredictionServiceUseUrlOverride",
base::FEATURE_DISABLED_BY_DEFAULT};
+#if defined(OS_ANDROID)
+// When enabled, the Default Search Engine does not automatically receive the
+// "geolocation" and "notifications" permissions. DSE only applies to Android.
+const base::Feature kRevertDSEAutomaticPermissions{
+ "RevertDSEAutomaticPermissions", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_ANDROID)
+
} // namespace features
namespace feature_params {
diff --git a/components/permissions/features.h b/components/permissions/features.h
index bc63e26..3b4485c 100644
--- a/components/permissions/features.h
+++ b/components/permissions/features.h
@@ -5,8 +5,10 @@
#ifndef COMPONENTS_PERMISSIONS_FEATURES_H_
#define COMPONENTS_PERMISSIONS_FEATURES_H_
+#include "base/component_export.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
+#include "build/build_config.h"
namespace permissions {
namespace features {
@@ -20,6 +22,11 @@
extern const base::Feature kPermissionChipRequestTypeSensitive;
extern const base::Feature kPermissionPredictionServiceUseUrlOverride;
+#if defined(OS_ANDROID)
+COMPONENT_EXPORT(PERMISSIONS_COMMON)
+extern const base::Feature kRevertDSEAutomaticPermissions;
+#endif // defined(OS_ANDROID)
+
} // namespace features
namespace feature_params {