[ads] Add [virtual]:feature=<name> condition matcher virtual pref (#35896)

Adds `[virtual]:feature=<feature_name>|is_overridden` and
`[virtual]:feature=<feature_name>|params|<param_name>` virtual pref paths
so campaign authors can check whether a named feature flag has been
explicitly overridden via Griffin, a command-line switch, or a
compile-time default, and inspect its field trial parameter values.
Any feature name is supported without a hardcoded list.
This commit is contained in:
Terry Mancey
2026-04-24 23:26:09 +01:00
committed by GitHub
parent 8ea5a96ef4
commit 6cd292c52e
8 changed files with 241 additions and 0 deletions
+1
View File
@@ -20,6 +20,7 @@ A place to define all specific terms and vocabulary for the Brave Ads component,
| Catalog | A collection of available campaigns, creative sets and creative instances. |
| Click | Refers to a user interacting with an advertisement by clicking on the ad. |
| Click-through rate | The percentage of ad impressions that result in clicks. |
| Condition matcher | One or more pref path and condition pairs, evaluated with AND logic on the user's device, that must all match before a new tab page ad can be served. Paths may reference stored prefs or virtual prefs computed at runtime, and conditions may use epoch, numerical, regex, or pattern operators. |
| Confirmations | Confirm events, i.e., views, without revealing to Brave the particular user involved. See [security and privacy model for ad confirmations](https://github.com/brave/brave-browser/wiki/Security-and-privacy-model-for-ad-confirmations). |
| Contextual | Contextual advertising targets ads based on the web page's content or the user's online activity context to deliver relevant and personalized advertisements. |
| Conversion | When a user triggers an action, it is counted as a conversion. Conversions include making a purchase or signing up for a newsletter. |
@@ -874,6 +874,8 @@ static_library("internal") {
"serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h",
"serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc",
"serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h",
"serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal.cc",
"serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal.h",
"serving/targeting/condition_matcher/prefs/internal/condition_matcher_keyword_path_component_util.cc",
"serving/targeting/condition_matcher/prefs/internal/condition_matcher_keyword_path_component_util.h",
"serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc",
@@ -13,6 +13,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "brave/components/brave_ads/core/internal/ads_client/ads_client_util.h"
#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal.h"
#include "brave/components/brave_ads/core/public/ads_client/ads_client.h"
namespace brave_ads {
@@ -390,6 +391,10 @@ bool HasLocalStatePrefPath(const std::string& path) {
std::optional<base::Value> GetVirtualPref(const base::DictValue& virtual_prefs,
std::string_view path) {
if (path.starts_with(kVirtualPrefPathPrefix)) {
if (std::optional<base::Value> dict = MaybeGetFeaturePrefValue(path)) {
return dict;
}
if (const base::Value* const value = virtual_prefs.Find(path)) {
return value->Clone();
}
@@ -239,6 +239,38 @@ class DictValue;
// }
// ]
//
// 9. "[virtual]:feature=<feature_name>|is_overridden"
// - Returns "1" if the named feature flag has been explicitly overridden
// (via Griffin, a command-line switch, or a compile-time default
// override), or "0" otherwise. Any feature name is supported, including
// future ones not yet known at build time. For example, the following
// condition matcher will match when `NotificationAdFeature` is
// overridden:
//
// "conditionMatchers": [
// {
// "condition": "1",
// "prefPath":
// "[virtual]:feature=NotificationAdFeature|is_overridden"
// }
// ]
//
// 10. "[virtual]:feature=<feature_name>|params|<param_name>"
// - Returns the string value of the named field trial param for the given
// feature, or does not match if the feature has no associated trial or
// the param does not exist. Combine with numerical operators to compare
// numeric param values. For example, the following condition matcher
// will match when the `version` param for `NotificationAdServing`
// equals 2:
//
// "conditionMatchers": [
// {
// "condition": "[R=]:2",
// "prefPath":
// "[virtual]:feature=NotificationAdServing|params|version"
// }
// ]
//
// NOTE: To identify condition matchers, first create a copy of your
// brave://local-state and `Default/Preferences` files. Next, change a
// brave://setting or enable a feature, quit the browser and then compare the
@@ -0,0 +1,62 @@
/* Copyright (c) 2026 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 "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal.h"
#include <string_view>
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_keyword_path_component_util.h"
namespace brave_ads {
namespace {
constexpr std::string_view kIsOverriddenKey = "is_overridden";
constexpr std::string_view kParamsKey = "params";
} // namespace
std::optional<base::Value> MaybeGetFeaturePrefValue(
std::string_view path_component) {
std::optional<std::string_view> feature_name =
MaybeParseKeywordPathComponentValue(path_component,
kFeatureVirtualPrefKeyword);
if (!feature_name || feature_name->empty()) {
return std::nullopt;
}
base::FeatureList* const feature_list = base::FeatureList::GetInstance();
const bool is_overridden = feature_list != nullptr &&
feature_list->IsFeatureOverridden(*feature_name);
base::DictValue dict = base::DictValue().Set(
kIsOverriddenKey, base::NumberToString(static_cast<int>(is_overridden)));
if (feature_list) {
const base::FieldTrial* const field_trial =
feature_list->GetAssociatedFieldTrialByFeatureName(*feature_name);
if (field_trial != nullptr) {
base::FieldTrialParams field_trial_params;
if (base::GetFieldTrialParams(field_trial->trial_name(),
&field_trial_params)) {
base::DictValue params_dict;
for (const auto& [param_name, param_value] : field_trial_params) {
params_dict.Set(param_name, param_value);
}
dict.Set(kParamsKey, std::move(params_dict));
}
}
}
return base::Value(std::move(dict));
}
} // namespace brave_ads
@@ -0,0 +1,37 @@
/* Copyright (c) 2026 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/. */
#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_FEATURE_PREF_UTIL_INTERNAL_H_
#define BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_FEATURE_PREF_UTIL_INTERNAL_H_
#include <optional>
#include <string_view>
namespace base {
class Value;
} // namespace base
// Resolves `[virtual]:feature=<feature_name>` keyword path components into a
// dict whose sub-paths are traversed via the normal `|` separator. Any feature
// name is supported, including future ones not yet known at build time.
namespace brave_ads {
inline constexpr std::string_view kFeatureVirtualPrefKeyword =
"[virtual]:feature";
// Returns a dict with an `is_overridden` string, either "1" if overridden or
// "0" if not; unknown feature names are treated as not overridden. When a
// field trial is associated the dict also contains a `params` sub-dict of
// param name-value pairs; otherwise `params` is absent and any
// `|params|<param_name>` traversal returns nothing. Returns `std::nullopt` if
// `path_component` does not match `[virtual]:feature=<feature_name>` or the
// feature name is empty.
std::optional<base::Value> MaybeGetFeaturePrefValue(
std::string_view path_component);
} // namespace brave_ads
#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_INTERNAL_SERVING_TARGETING_CONDITION_MATCHER_PREFS_INTERNAL_CONDITION_MATCHER_FEATURE_PREF_UTIL_INTERNAL_H_
@@ -0,0 +1,101 @@
/* Copyright (c) 2026 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 "brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal.h"
#include "base/feature_list.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "brave/components/brave_ads/core/internal/common/test/test_base.h"
// npm run test -- brave_all_unit_tests --filter=BraveAds*
namespace brave_ads {
namespace {
BASE_FEATURE(kTestFeature, base::FEATURE_ENABLED_BY_DEFAULT);
} // namespace
class BraveAdsConditionMatcherFeaturePrefUtilInternalTest
: public test::TestBase {};
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
GetIsOverriddenWhenFeatureIsOverridden) {
// Arrange
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kTestFeature);
// Act
std::optional<base::Value> feature_dict =
MaybeGetFeaturePrefValue("[virtual]:feature=TestFeature");
// Assert
ASSERT_TRUE(feature_dict);
EXPECT_EQ("1", *feature_dict->GetDict().FindString("is_overridden"));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
GetIsNotOverriddenWhenFeatureIsNotOverridden) {
// Act
std::optional<base::Value> feature_dict =
MaybeGetFeaturePrefValue("[virtual]:feature=TestFeature");
// Assert
ASSERT_TRUE(feature_dict);
EXPECT_EQ("0", *feature_dict->GetDict().FindString("is_overridden"));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
GetParamsWhenFeatureHasTrial) {
// Arrange
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(kTestFeature,
{{"foo", "bar"}});
// Act
std::optional<base::Value> feature_dict =
MaybeGetFeaturePrefValue("[virtual]:feature=TestFeature");
// Assert
ASSERT_TRUE(feature_dict);
const auto* const params_dict = feature_dict->GetDict().FindDict("params");
ASSERT_TRUE(params_dict);
EXPECT_EQ("bar", *params_dict->FindString("foo"));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
DoNotGetParamsDictWhenFeatureHasNoTrial) {
// Arrange
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kTestFeature);
// Act
std::optional<base::Value> feature_dict =
MaybeGetFeaturePrefValue("[virtual]:feature=TestFeature");
// Assert
ASSERT_TRUE(feature_dict);
EXPECT_FALSE(feature_dict->GetDict().FindDict("params"));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
DoNotGetFeaturePrefValueForNonKeywordPath) {
// Act & Assert
EXPECT_FALSE(MaybeGetFeaturePrefValue("TestFeature"));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
DoNotGetFeaturePrefValueForEmptyFeatureName) {
// Act & Assert
EXPECT_FALSE(MaybeGetFeaturePrefValue("[virtual]:feature="));
}
TEST_F(BraveAdsConditionMatcherFeaturePrefUtilInternalTest,
DoNotGetFeaturePrefValueForKeywordWithNoEqualsSign) {
// Act & Assert
EXPECT_FALSE(MaybeGetFeaturePrefValue("[virtual]:feature"));
}
} // namespace brave_ads
+1
View File
@@ -530,6 +530,7 @@ source_set("brave_ads_unit_tests") {
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_feature_pref_util_internal_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_keyword_path_component_util_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc",
"//brave/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_time_period_storage_pref_util_internal_unittest.cc",