Skip to content

Commit

Permalink
Add support for default feature state overrides using static map (#10878
Browse files Browse the repository at this point in the history
)

* Add support for default feature state override.

* Convert runtime feature overrides to static overrides.

* Support overrides set via a single macro instantiation.

* Remove unused include.
  • Loading branch information
goodov authored Nov 11, 2021
1 parent 7f94dd5 commit ad2ce27
Show file tree
Hide file tree
Showing 35 changed files with 611 additions and 93 deletions.
97 changes: 6 additions & 91 deletions app/brave_main_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,23 @@
#include "brave/renderer/brave_content_renderer_client.h"
#include "brave/utility/brave_content_utility_client.h"
#include "build/build_config.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/component_updater/component_updater_switches.h"
#include "components/dom_distiller/core/dom_distiller_switches.h"
#include "components/embedder_support/switches.h"
#include "components/federated_learning/features/features.h"
#include "components/feed/feed_feature_list.h"
#include "components/language/core/common/language_experiments.h"
#include "components/network_time/network_time_tracker.h"
#include "components/offline_pages/core/offline_page_feature.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/reading_list/features/reading_list_switches.h"
#include "components/security_state/core/features.h"
#include "components/sync/base/sync_base_switches.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "components/variations/variations_switches.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "google_apis/gaia/gaia_switches.h"
#include "media/base/media_switches.h"
#include "net/base/features.h"
#include "services/device/public/cpp/device_features.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/ui_base_features.h"

#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "brave/build/android/jni_headers/BraveQAPreferences_jni.h"
#include "components/signin/public/base/account_consistency_method.h"
#else
#include "chrome/browser/apps/app_discovery_service/app_discovery_features.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/ui/profile_picker.h"
#endif

Expand Down Expand Up @@ -210,76 +188,13 @@ bool BraveMainDelegate::BasicStartupComplete(int* exit_code) {
command_line.AppendSwitchASCII(variations::switches::kVariationsServerURL,
kVariationsServerURL.c_str());

// Enabled features.
std::unordered_set<const char*> enabled_features = {
// Upgrade all mixed content
blink::features::kMixedContentAutoupgrade.name,
password_manager::features::kPasswordImport.name,
net::features::kLegacyTLSEnforced.name,
// Enable webui dark theme: @media (prefers-color-scheme: dark) is gated
// on this feature.
features::kWebUIDarkMode.name,
blink::features::kPrefetchPrivacyChanges.name,
blink::features::kReducedReferrerGranularity.name,
#if defined(OS_WIN)
features::kWinrtGeolocationImplementation.name,
#endif
security_state::features::kSafetyTipUI.name,
};
// Runtime-enabled features. To override Chromium features default state
// please see: brave/chromium_src/base/feature_override.h
std::unordered_set<const char*> enabled_features = {};

// Disabled features.
std::unordered_set<const char*> disabled_features = {
#if !defined(OS_ANDROID)
apps::kAppDiscoveryRemoteUrlSearch.name,
#endif
autofill::features::kAutofillEnableAccountWalletStorage.name,
autofill::features::kAutofillServerCommunication.name,
blink::features::kAdInterestGroupAPI.name,
blink::features::kComputePressure.name,
blink::features::kConversionMeasurement.name,
blink::features::kFledge.name,
blink::features::kHandwritingRecognitionWebPlatformApiFinch.name,
blink::features::kInterestCohortAPIOriginTrial.name,
blink::features::kInterestCohortFeaturePolicy.name,
blink::features::kInterestGroupStorage.name,
blink::features::kNavigatorPluginsFixed.name,
blink::features::kParakeet.name,
blink::features::kPrerender2.name,
blink::features::kReportAllJavaScriptFrameworks.name,
blink::features::kSpeculationRulesPrefetchProxy.name,
blink::features::kTextFragmentAnchor.name,
blink::features::kWebSQLInThirdPartyContextEnabled.name,
#if !defined(OS_ANDROID)
features::kCopyLinkToText.name,
#endif
features::kDirectSockets.name,
features::kIdleDetection.name,
features::kNotificationTriggers.name,
features::kSignedExchangeSubresourcePrefetch.name,
features::kSubresourceWebBundles.name,
#if defined(OS_ANDROID)
features::kWebNfc.name,
#endif
features::kWebOTP.name,
features::kTabGroupsFeedback.name,
federated_learning::kFederatedLearningOfCohorts.name,
federated_learning::kFlocIdComputedEventLogging.name,
#if defined(OS_ANDROID)
feed::kInterestFeedContentSuggestions.name,
feed::kInterestFeedV2.name,
#endif
media::kLiveCaption.name,
net::features::kFirstPartySets.name,
network::features::kTrustTokens.name,
network_time::kNetworkTimeServiceQuerying.name,
#if defined(OS_ANDROID)
offline_pages::kPrefetchingOfflinePagesFeature.name,
#endif
reading_list::switches::kReadLater.name,
#if defined(OS_ANDROID)
translate::kTranslate.name,
#endif
};
// Runtime-disabled features. To override Chromium features default state
// please see: brave/chromium_src/base/feature_override.h
std::unordered_set<const char*> disabled_features = {};

if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableDnsOverHttps)) {
Expand Down
12 changes: 10 additions & 2 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

source_set("base_unittests") {
testonly = true
sources = [ "tools_sanity_unittest.cc" ]
deps = [ "//testing/gtest:gtest" ]
sources = [
"feature_override_unittest.cc",
"tools_sanity_unittest.cc",
]
deps = [
"//base",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
90 changes: 90 additions & 0 deletions base/feature_override_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "base/feature_override.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/test/mock_callback.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

namespace base {
namespace {

const Feature kTestControlEnabledFeature{"TestControlEnabledFeature",
FEATURE_ENABLED_BY_DEFAULT};
const Feature kTestControlDisabledFeature{"TestControlDisabledFeature",
FEATURE_DISABLED_BY_DEFAULT};

const Feature kTestEnabledButOverridenFeature{"TestEnabledButOverridenFeature",
FEATURE_DISABLED_BY_DEFAULT};
const Feature kTestDisabledButOverridenFeature{
"TestDisabledButOverridenFeature", FEATURE_ENABLED_BY_DEFAULT};

constexpr Feature kTestConstexprEnabledButOverridenFeature{
"TestConstexprEnabledButOverridenFeature", FEATURE_DISABLED_BY_DEFAULT};
constexpr Feature kTestConstexprDisabledButOverridenFeature{
"TestConstexprDisabledButOverridenFeature", FEATURE_ENABLED_BY_DEFAULT};

OVERRIDE_FEATURE_DEFAULT_STATES({{
{kTestEnabledButOverridenFeature, FEATURE_DISABLED_BY_DEFAULT},
{kTestDisabledButOverridenFeature, FEATURE_ENABLED_BY_DEFAULT},
{kTestConstexprEnabledButOverridenFeature, FEATURE_DISABLED_BY_DEFAULT},
{kTestConstexprDisabledButOverridenFeature, FEATURE_ENABLED_BY_DEFAULT},
}});

} // namespace

TEST(FeatureOverrideTest, OverridesTest) {
struct TestCase {
const Feature& feature;
const bool is_enabled;
};
const TestCase kTestCases[] = {
// Untouched features.
{kTestControlEnabledFeature, true},
{kTestControlDisabledFeature, false},

// Overridden features.
{kTestEnabledButOverridenFeature, false},
{kTestDisabledButOverridenFeature, true},

// Overridden constexpr features.
{kTestConstexprEnabledButOverridenFeature, false},
{kTestConstexprDisabledButOverridenFeature, true},
};
for (const auto& test_case : kTestCases) {
EXPECT_EQ(test_case.is_enabled, FeatureList::IsEnabled(test_case.feature))
<< test_case.feature.name;
}
}

#if DCHECK_IS_ON()
TEST(FeatureOverrideTest, FeatureDuplicateDChecks) {
base::MockCallback<logging::LogAssertHandlerFunction> mock_log_handler;
logging::ScopedLogAssertHandler scoped_log_handler(mock_log_handler.Get());
EXPECT_CALL(
mock_log_handler,
Run(_, _,
testing::HasSubstr("TestEnabledButOverridenFeature is duplicated"),
_));
EXPECT_CALL(
mock_log_handler,
Run(_, _,
testing::HasSubstr(
"TestEnabledButOverridenFeature has already been overridden"),
_))
.Times(2);

internal::FeatureDefaultStateOverrider test_overrider{{
{kTestEnabledButOverridenFeature, FEATURE_DISABLED_BY_DEFAULT},
{kTestEnabledButOverridenFeature, FEATURE_DISABLED_BY_DEFAULT},
}};
}
#endif // DCHECK_IS_ON()

} // namespace base
74 changes: 74 additions & 0 deletions chromium_src/base/feature_list.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "base/feature_list.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/feature_override.h"
#include "base/no_destructor.h"

namespace base {
namespace internal {
namespace {

using DefaultStateOverrides =
base::flat_map<std::reference_wrapper<const Feature>, FeatureState>;

DefaultStateOverrides& GetDefaultStateOverrides() {
static base::NoDestructor<DefaultStateOverrides> default_state_overrides;
return *default_state_overrides;
}

inline FeatureState GetDefaultOrOverriddenFeatureState(const Feature& feature) {
const auto& default_state_overrides = GetDefaultStateOverrides();
const auto default_state_override_it = default_state_overrides.find(feature);
return default_state_override_it != default_state_overrides.end()
? default_state_override_it->second
: feature.default_state;
}

} // namespace

FeatureDefaultStateOverrider::FeatureDefaultStateOverrider(
std::initializer_list<FeatureOverrideInfo> overrides) {
auto& default_state_overrides = GetDefaultStateOverrides();
#if DCHECK_IS_ON()
{
base::flat_set<std::reference_wrapper<const Feature>> new_overrides;
new_overrides.reserve(overrides.size());
for (const auto& override : overrides) {
DCHECK(new_overrides.insert(override.first).second)
<< "Feature " << override.first.get().name
<< " is duplicated in the current override macros";
DCHECK(!base::Contains(default_state_overrides, override.first))
<< "Feature " << override.first.get().name
<< " has already been overridden";
}
}
#endif
default_state_overrides.insert(overrides.begin(), overrides.end());
}

} // namespace internal

// Custom comparator to use std::reference_wrapper as a key in a map/set.
static inline bool operator<(const std::reference_wrapper<const Feature>& lhs,
const std::reference_wrapper<const Feature>& rhs) {
// Compare internal pointers directly, because there must only ever be one
// struct instance for a given feature name.
return &lhs.get() < &rhs.get();
}

} // namespace base

// This replaces |default_state| compare blocks with a modified one that
// includes the state override check.
#define default_state \
name&& internal::GetDefaultOrOverriddenFeatureState(feature)

#include "../../../base/feature_list.cc"

#undef default_state
59 changes: 59 additions & 0 deletions chromium_src/base/feature_override.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_CHROMIUM_SRC_BASE_FEATURE_OVERRIDE_H_
#define BRAVE_CHROMIUM_SRC_BASE_FEATURE_OVERRIDE_H_

#include <functional>
#include <initializer_list>
#include <utility>

#include "base/feature_list.h"

// Helpers to override base::Feature::default_state without patches.
//
// Usage:
// 1. Create chromium_src/.../features.cc override for a file that contains
// features to override.
// 2. #include "base/feature_override.h"
// 3. Use OVERRIDE_FEATURE_DEFAULT_STATES macro:
//
// OVERRIDE_FEATURE_DEFAULT_STATES({{
// {kUpstreamFeature, base::FEATURE_ENABLED_BY_DEFAULT},
// #if defined(OS_ANDROID)
// {kAnotherUpstreamFeature, base::FEATURE_DISABLED_BY_DEFAULT},
// #endif
// }});

namespace base {
namespace internal {

// Perform base::Feature duplicates check and fills overriden states into a
// map that is used at runtime to get an override if available.
class BASE_EXPORT FeatureDefaultStateOverrider {
public:
using FeatureOverrideInfo =
std::pair<std::reference_wrapper<const Feature>, FeatureState>;

FeatureDefaultStateOverrider(
std::initializer_list<FeatureOverrideInfo> overrides);
};

} // namespace internal
} // namespace base

// Feature override uses global constructors, we disable `global-constructors`
// warning inside this macro to instantiate the overrider without warnings.
// clang-format off
#define OVERRIDE_FEATURE_DEFAULT_STATES(...) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
static const ::base::internal::FeatureDefaultStateOverrider \
g_feature_default_state_overrider __VA_ARGS__; \
_Pragma("clang diagnostic pop") \
static_assert(true, "") /* for a semicolon requirement */
// clang-format on

#endif // BRAVE_CHROMIUM_SRC_BASE_FEATURE_OVERRIDE_H_
1 change: 1 addition & 0 deletions chromium_src/chrome/browser/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include_rules = [
"+../../../../../../chrome/browser/android/omnibox",
"+../../../../../../chrome/browser/android/preferences",
"+../../../../../../chrome/browser/android/signin",
"+../../../../../../chrome/browser/apps/app_discovery_service",
"+../../../../../chrome/browser/autocomplete",
"+../../../../../chrome/browser/bitmap_fetcher",
"+../../../../../chrome/browser/bookmarks",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "../../../../../../chrome/browser/apps/app_discovery_service/app_discovery_features.cc"

#include "base/feature_override.h"

namespace apps {

OVERRIDE_FEATURE_DEFAULT_STATES({{
#if !defined(OS_ANDROID)
{kAppDiscoveryRemoteUrlSearch, base::FEATURE_DISABLED_BY_DEFAULT},
#endif
}});

} // namespace apps
Loading

0 comments on commit ad2ce27

Please sign in to comment.