blob: 9ccd012c565af6bf3ab795f3d1f6af8c6fdd3a4d [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
#pragma allow_unsafe_libc_calls
#endif
#include "components/feature_engagement/public/feature_configurations.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/feature_engagement/public/configuration.h"
#include "components/feature_engagement/public/event_constants.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/feature_engagement/public/group_constants.h"
#if BUILDFLAG(IS_IOS)
#include "base/metrics/field_trial_params.h"
#include "components/feature_engagement/public/ios_promo_feature_configuration.h"
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_CHROMEOS)
#include "components/feature_engagement/public/scalable_iph_feature_configurations.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace {
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
const int k10YearsInDays = 365 * 10;
#endif
} // namespace
namespace feature_engagement {
FeatureConfig CreateAlwaysTriggerConfig(const base::Feature* feature) {
// Trim "IPH_" prefix from the feature name to use for trigger and used
// events.
const char* prefix = "IPH_";
std::string stripped_feature_name = feature->name;
if (base::StartsWith(stripped_feature_name, prefix,
base::CompareCase::SENSITIVE)) {
stripped_feature_name = stripped_feature_name.substr(strlen(prefix));
}
// A config that always meets condition to trigger IPH.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig(stripped_feature_name + "_trigger",
Comparator(ANY, 0), 90, 90);
config.used =
EventConfig(stripped_feature_name + "_used", Comparator(ANY, 0), 90, 90);
return config;
}
#if BUILDFLAG(IS_IOS)
std::optional<FeatureConfig> CreateNewUserGestureInProductHelpConfig(
const base::Feature& feature,
const char* action_event,
const char* trigger_event,
const char* used_event,
const char* dismiss_button_tap_event) {
// Maximum storage days for iOS gesture IPHs in days. Note that they only
// triggered for users who installed Chrome on iOS in the last specific number
// of days, so this could be used as the maximum storage period of respective
// events.
const int kTotalMaxOccurrences = 2;
const uint32_t kMaxStorageDays = 61;
// The IPH only shows once a week, and honors `kTotalMaxOccurrences`.
const int kDaysBetweenOccurrences = 7;
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// The user hasn't done the action suggested by the IPH.
config.used = EventConfig(used_event, Comparator(EQUAL, 0), kMaxStorageDays,
kMaxStorageDays);
// The IPH shows at most once per `kDaysBetweenOccurrences`.
config.trigger =
EventConfig(trigger_event, Comparator(EQUAL, 0), kDaysBetweenOccurrences,
kDaysBetweenOccurrences);
config.event_configs.insert(
EventConfig(trigger_event, Comparator(LESS_THAN, kTotalMaxOccurrences),
kMaxStorageDays, kMaxStorageDays));
// The IPH only shows when user performs the action that should trigger the
// IPH at least twice since the last time the IPH shows, or since installation
// if it hasn't.
config.event_configs.insert(
EventConfig(action_event, Comparator(GREATER_THAN_OR_EQUAL, 2),
kDaysBetweenOccurrences, kDaysBetweenOccurrences));
// The user hasn't explicitly dismissed the same IPH before.
config.event_configs.insert(EventConfig(dismiss_button_tap_event,
Comparator(EQUAL, 0), kMaxStorageDays,
kMaxStorageDays));
return config;
}
#endif
std::optional<FeatureConfig> GetClientSideFeatureConfig(
const base::Feature* feature) {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
// The IPH bubble for link capturing has a trigger set to ANY so that it
// always shows up. The per app specific guardrails are independently stored
// under the web_app_prefs.
if (kIPHDesktopPWAsLinkCapturingLaunch.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("desktop_pwa_launch_link_capturing",
Comparator(ANY, 0), 0, 0);
config.used = EventConfig("desktop_pwa_launch_link_capturing_used",
Comparator(ANY, 0), 0, 0);
return config;
}
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
if (kIPHPasswordsManagementBubbleAfterSaveFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.trigger =
EventConfig("password_saved", Comparator(LESS_THAN, 1), 360, 360);
config.session_rate = Comparator(ANY, 0);
config.availability = Comparator(ANY, 0);
return config;
}
if (kIPHPasswordsManagementBubbleDuringSigninFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.trigger =
EventConfig("signin_flow_detected", Comparator(LESS_THAN, 1), 360, 360);
config.session_rate = Comparator(ANY, 0);
config.availability = Comparator(ANY, 0);
return config;
}
if (kIPHProfileSwitchFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the promo once a year if the profile menu was not opened.
config.trigger =
EventConfig("profile_switch_trigger", Comparator(EQUAL, 0), 360, 360);
config.used =
EventConfig("profile_menu_shown", Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHReadingListInSidePanelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the promo once a year if the side panel was not opened.
config.trigger =
EventConfig("side_panel_trigger", Comparator(EQUAL, 0), 360, 360);
config.used =
EventConfig("side_panel_shown", Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHReadingModeSidePanelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// Show the promo up to 3 times a year.
config.trigger = EventConfig("iph_reading_mode_side_panel_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used = EventConfig("reading_mode_side_panel_shown",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHSidePanelGenericPinnableFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show the promo once a year if the side panel was not opened.
config.trigger = EventConfig("side_panel_pinnable_trigger",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig(feature_engagement::events::kSidePanelPinned,
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHSignoutWebInterceptFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig("iph_signout_web_intercept_triggered",
Comparator(ANY, 0), 0, 0);
config.used =
EventConfig("iph_signout_web_intercept_used", Comparator(ANY, 0), 0, 0);
return config;
}
if (kIPHGMCCastStartStopFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("gmc_start_stop_iph_trigger",
Comparator(EQUAL, 0), 180, 180);
config.used = EventConfig("media_route_stopped_from_gmc",
Comparator(EQUAL, 0), 180, 180);
return config;
}
if (kIPHGMCLocalMediaCastingFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig("gmc_local_media_cast_iph_trigger",
Comparator(EQUAL, 0), 180, 180);
config.used = EventConfig("media_route_started_from_gmc",
Comparator(EQUAL, 0), 180, 180);
return config;
}
if (kIPHDesktopSharedHighlightingFeature.name == feature->name) {
// A config that allows the shared highlighting desktop IPH to be shown
// when a user receives a highlight:
// * Once per 7 days
// * Up to 5 times but only if unused in the last 7 days.
// * Used fewer than 2 times
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("iph_desktop_shared_highlighting_trigger",
Comparator(LESS_THAN, 5), 360, 360);
config.used = EventConfig("iph_desktop_shared_highlighting_used",
Comparator(LESS_THAN, 2), 360, 360);
config.event_configs.insert(
EventConfig("iph_desktop_shared_highlighting_trigger",
Comparator(EQUAL, 0), 7, 360));
return config;
}
if (kIPHExperimentalAIPromoFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// Show the IPH once per year.
config.trigger = EventConfig("iph_experimental_ai_promo_trigger",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("iph_experimental_ai_promo_shown",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHBatterySaverModeFeature.name == feature->name) {
// Show promo once a year when the battery saver toolbar icon is visible.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("battery_saver_info_triggered",
Comparator(LESS_THAN, 1), 360, 360);
config.used =
EventConfig("battery_saver_info_shown", Comparator(EQUAL, 0), 7, 360);
return config;
}
if (kIPHMemorySaverModeFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the promo max 3 times, once per week.
config.trigger = EventConfig("high_efficiency_prompt_in_trigger",
Comparator(LESS_THAN, 1), 7, 360);
// This event is never logged but is included for consistency.
config.used = EventConfig("high_efficiency_prompt_in_used",
Comparator(EQUAL, 0), 360, 360);
config.event_configs.insert(EventConfig("high_efficiency_prompt_in_trigger",
Comparator(LESS_THAN, 1), 360,
360));
return config;
}
if (kIPHPerformanceInterventionDialogFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show intervention dialog at most once per day and no more than 5 times
// per week.
config.trigger = EventConfig("performance_intervention_dialog_trigger",
Comparator(EQUAL, 0), 1, 360);
config.event_configs.insert(
EventConfig("performance_intervention_dialog_trigger",
Comparator(LESS_THAN, 5), 7, 360));
return config;
}
if (kIPHPowerBookmarksSidePanelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("iph_power_bookmarks_side_panel_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used = EventConfig("power_bookmarks_side_panel_shown",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHPriceInsightsPageActionIconLabelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the label once per day, 3 times max in 28 days.
config.trigger =
EventConfig("price_insights_page_action_icon_label_in_trigger",
Comparator(ANY, 0), 0, 360);
config.used = EventConfig("price_insights_page_action_icon_label_used",
Comparator(ANY, 0), 0, 360);
config.event_configs.insert(
EventConfig("price_insights_page_action_icon_label_in_trigger",
Comparator(ANY, 0), 0, 360));
return config;
}
if (kIPHPriceTrackingEmailConsentFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show the IPH up to 3 times per month.
config.trigger = EventConfig("price_tracking_email_consent_trigger",
Comparator(LESS_THAN, 3), 30, 360);
return config;
}
if (kIPHPriceTrackingInSidePanelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the promo once a year if the price tracking IPH was not triggered.
config.trigger = EventConfig("iph_price_tracking_side_panel_trigger",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("price_tracking_side_panel_shown",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHPriceTrackingPageActionIconLabelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
// Show the promo once per day.
config.trigger =
EventConfig("price_tracking_page_action_icon_label_in_trigger",
Comparator(LESS_THAN, 1), 1, 360);
return config;
}
if (kIPHShoppingCollectionFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show the IPH 3 times per year.
config.trigger = EventConfig("shopping_collection_trigger",
Comparator(LESS_THAN, 3), 360, 360);
return config;
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (kIPHExtensionsMenuFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
// Show promo up to three times a year or until the extensions menu is
// opened while any extension has access to the current site.
config.trigger = EventConfig("extensions_menu_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used =
EventConfig("extensions_menu_opened_while_extension_has_access",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHExtensionsRequestAccessButtonFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
// Show promo up to three times a year or until the request access button
// is clicked.
config.trigger = EventConfig("extensions_request_access_button_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used = EventConfig("extensions_request_access_button_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
#endif
if (kIPHCompanionSidePanelFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show the promo up to 3 times a year.
config.trigger = EventConfig("iph_companion_side_panel_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used =
EventConfig("companion_side_panel_accessed_via_toolbar_button",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHCompanionSidePanelRegionSearchFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// Show the promo up to 3 times a year.
config.trigger =
EventConfig("iph_companion_side_panel_region_search_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used =
EventConfig("companion_side_panel_region_search_button_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHPasswordsWebAppProfileSwitchFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("iph_passwords_web_app_profile_switch_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("web_app_profile_menu_shown",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHPasswordManagerShortcutFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("iph_password_manager_shortcut_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("password_manager_shortcut_created",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHPasswordSharingFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig("password_sharing_iph_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("password_share_button_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHDiscardRingFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("discard_ring_trigger", Comparator(EQUAL, 0), 360, 360);
// This event is never logged but is included for consistency.
config.used =
EventConfig("discard_ring_used", Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHDownloadEsbPromoFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
// Because this is a custom configuration being used in desktop user ed, use
// a non-default availability so the configurator doesn't try to write its
// own.
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
// Don't show if user has already seen an IPH this session.
// Show the promo max once a year if the user hasn't interacted with
// a dangerous download within the last 21 days.
config.trigger = EventConfig("download_bubble_esb_iph_trigger",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("enable_enhanced_protection",
Comparator(EQUAL, 0), 21, 360);
config.event_configs.insert(
EventConfig("download_bubble_dangerous_download_detected",
Comparator(GREATER_THAN_OR_EQUAL, 1), 21, 360));
return config;
}
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (kEsbDownloadRowPromoFeature.name == feature->name) {
// A config that allows a promotion row referring users to enable Enhanced
// Safe Browsing (ESB), to be shown on the Downloads manager page. It
// can be viewed at most 7 times without interaction across a 90 day period.
// If the user clicks, then we aritificially increment the viewed event by 4
// so that the row can be seen at most 2 more times.
//
// The trigger management can be found in
// c/b/ui/webui/downloads/downloads_dom_handler.cc
FeatureConfig config;
config.valid = true;
// Because this is a custom configuration being used in desktop user ed, use
// a non-default availability so the configurator doesn't try to write its
// own.
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
// This isn't an IPH so we don't suppress other engagement features.
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
// This isn't an IPH so we don't want to block or be blocked by any other
// engagement features.
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
config.trigger = EventConfig("dangerous_download_esb_promo_row_trigger",
Comparator(ANY, 0), 360, 360);
config.used =
EventConfig("enable_enhanced_protection", Comparator(EQUAL, 0), 21, 90);
config.event_configs.insert(EventConfig("esb_download_promo_row_viewed",
Comparator(LESS_THAN, 7), 90, 90));
config.event_configs.insert(EventConfig("esb_download_promo_row_clicked",
Comparator(LESS_THAN, 3), 90, 90));
return config;
}
#endif
if (kIPHBackNavigationMenuFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("back_navigation_menu_iph_is_triggered",
Comparator(LESS_THAN_OR_EQUAL, 4), 360, 360);
config.used = EventConfig("back_navigation_menu_is_opened",
Comparator(EQUAL, 0), 7, 360);
config.snooze_params.snooze_interval = 7;
config.snooze_params.max_limit = 4;
return config;
}
if (kIPHComposeNewBadgeFeature.name == feature->name) {
// A config that allows the new badge displayed in the Compose feature nudge
// to be shown at most 4 times in a 10-day window and only while the user
// has opened the Compose feature less than 3 times.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("compose_new_badge_triggered",
Comparator(LESS_THAN, 4), 10, 360);
config.used =
EventConfig("compose_activated", Comparator(LESS_THAN, 3), 360, 360);
return config;
}
if (kIPHComposeMSBBSettingsFeature.name == feature->name) {
// A config that allows a toast to be displayed in the Settings page when
// opened via the Compose MSBB feature
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig("compose_msbb_settings_feature_trigger",
Comparator(ANY, 0), 90, 90);
config.used = EventConfig("compose_msbb_settings_feature_used",
Comparator(ANY, 0), 90, 90);
return config;
}
if (kIPHTabOrganizationSuccessFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
// Show the IPH once per year.
config.trigger = EventConfig("iph_tab_organization_success_trigger",
Comparator(EQUAL, 0), 360, 360);
config.used =
EventConfig("tab_group_editor_shown", Comparator(EQUAL, 0), 360, 360);
return config;
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS)
#if !BUILDFLAG(IS_ANDROID)
if (kIPHiOSPasswordPromoDesktopFeature.name == feature->name) {
// A config for allowing other IPH's to explicitly block the iOS password
// promo bubble on desktop if needed. Blocked and blocking by default, so
// won't appear at the same time as other IPH, but without any session rate
// impact.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.blocked_by.type = BlockedBy::Type::ALL;
config.blocking.type = Blocking::Type::ALL;
config.used =
EventConfig("ios_password_promo_bubble_on_desktop_interacted_with",
Comparator(ANY, 0), 0, 0);
config.trigger = EventConfig("ios_password_promo_bubble_on_desktop_shown",
Comparator(ANY, 0), 0, 0);
return config;
}
if (kIPHiOSAddressPromoDesktopFeature.name == feature->name) {
// A config for allowing other IPH's to explicitly block the iOS address
// promo bubble on desktop if needed. Blocked and blocking by default, so
// won't appear at the same time as other IPH, but without any session rate
// impact.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.blocked_by.type = BlockedBy::Type::ALL;
config.blocking.type = Blocking::Type::ALL;
config.used =
EventConfig("ios_address_promo_bubble_on_desktop_interacted_with",
Comparator(ANY, 0), 0, 0);
config.trigger = EventConfig("ios_address_promo_bubble_on_desktop_shown",
Comparator(ANY, 0), 0, 0);
return config;
}
if (kIPHiOSPaymentPromoDesktopFeature.name == feature->name) {
// A config for allowing other IPH's to explicitly block the iOS payment
// promo bubble on desktop if needed. Blocked and blocking by default, so
// won't appear at the same time as other IPH, but without any session rate
// impact.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.blocked_by.type = BlockedBy::Type::ALL;
config.blocking.type = Blocking::Type::ALL;
config.used =
EventConfig("ios_payment_promo_bubble_on_desktop_interacted_with",
Comparator(ANY, 0), 0, 0);
config.trigger = EventConfig("ios_payment_promo_bubble_on_desktop_shown",
Comparator(ANY, 0), 0, 0);
return config;
}
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
if (kIPHAccountSettingsHistorySync.name == feature->name) {
// A config that allows the history sync opt-in toggle IPH to be shown
// only once when a user who is signed-in but not syncing history and tabs
// visits the account settings page.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("instance_switcher_iph_trigger", Comparator(LESS_THAN, 1),
k10YearsInDays, k10YearsInDays);
config.used = EventConfig("instance_switcher_used", Comparator(EQUAL, 0),
k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHAndroidTabDeclutter.name == feature->name) {
// Allows an IPH for tab declutter for the tab switcher button:
// * Only once per week.
// * Up to 3 times per year.
// * And only as long as the user has never manually accessed their
// archived tabs.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("android_tab_declutter_iph_triggered",
Comparator(EQUAL, 0), 7, 7);
config.event_configs.insert(
EventConfig("android_tab_declutter_iph_triggered",
Comparator(LESS_THAN, 3), 360, 360));
config.used = EventConfig("android_tab_declutter_button_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabGroupSyncOnStripFeature.name == feature->name) {
// A config that allows the TabGroupSync IPH to be shown up to 3 times per
// year.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("tab_group_sync_on_strip_iph_triggered",
Comparator(LESS_THAN, 3), 360, 360);
config.used = EventConfig("tab_groups_surface_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabGroupShareNoticeFeature.name == feature->name) {
// Allows an IPH for showing the tab group share notice. This will only be
// shown once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("tab_group_share_notice_iph_triggered",
Comparator(LESS_THAN, 1), k10YearsInDays, k10YearsInDays);
config.used =
EventConfig("tab_group_share_notice_dismissed", Comparator(EQUAL, 0),
k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHTabGroupShareNotificationBubbleOnStripFeature.name == feature->name) {
// A config to show IPH for TabGroupShare notification bubble when a shared
// group is updated by a group member. This will only be shown the first
// time the notification bubble is displayed.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig(
"tab_group_share_notification_bubble_on_strip_iph_triggered",
Comparator(LESS_THAN, 1), 360, 360);
config.used =
EventConfig("tab_group_share_notification_bubble_clicked",
Comparator(EQUAL, 0), k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHTabGroupShareUpdateFeature.name == feature->name) {
// Allows an IPH for showing the tab group share update explanation the
// first time the activity bubble appears. This will only be shown once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("tab_group_share_update_iph_triggered",
Comparator(LESS_THAN, 1), k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHTabGroupCreationDialogSyncTextFeature.name == feature->name) {
// A config that allows the sync text IPH on the TabGroupCreationDialog to
// be shown up to 3 times total (10 year max in place of unlimited window).
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("tab_group_creation_dialog_sync_text_iph_triggered",
Comparator(LESS_THAN, 3), 3600, 3600);
config.used = EventConfig("tab_group_creation_dialog_shown",
Comparator(LESS_THAN, 3), 3600, 3600);
return config;
}
if (kIPHAppSpecificHistory.name == feature->name) {
// A config that allows the AppSpecificHistory IPH to be shown once
// a week, up to 3 times, unless the button is clicked at least once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("app_specific_history_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.event_configs.insert(EventConfig("app_specific_history_iph_trigger",
Comparator(LESS_THAN, 1), 7, 360));
config.used = EventConfig("history_toolbar_search_menu_item_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHBottomToolbarTipFeature.name == feature->name) {
// A config that allows the Bottom Toolbar IPH to be shown once
// a week, up to 3 times, unless the user interacts with Bottom Toolbar
// controls.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("bottom_toolbar_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.event_configs.insert(EventConfig("bottom_toolbar_iph_trigger",
Comparator(LESS_THAN, 1), 7, 360));
config.used = EventConfig("bottom_toolbar_menu_triggered",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHCCTHistory.name == feature->name) {
// A config that allows the CCTHistory IPH to be shown once
// a week, up to 3 times, unless the button is clicked at least once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("cct_history_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.event_configs.insert(EventConfig("cct_history_iph_trigger",
Comparator(LESS_THAN, 1), 7, 360));
config.used = EventConfig("cct_history_menu_item_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHCCTMinimized.name == feature->name) {
// A config that allows the Custom Tab minimize button IPH to be shown once
// a day, up to 3 times, unless the button is clicked at least once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("cct_minimized_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.event_configs.insert(EventConfig("cct_minimized_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360));
config.used = EventConfig("cct_minimize_button_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHDataSaverDetailFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("data_saver_detail_iph_trigger",
Comparator(EQUAL, 0), 90, 360);
config.used = EventConfig("data_saver_overview_opened",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("data_saved_page_load",
Comparator(GREATER_THAN_OR_EQUAL, 10), 90, 360));
return config;
}
if (kIPHDataSaverPreviewFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("data_saver_preview_iph_trigger",
Comparator(EQUAL, 0), 90, 360);
config.used = EventConfig("data_saver_preview_opened",
Comparator(LESS_THAN_OR_EQUAL, 1), 90, 360);
return config;
}
if (kIPHPreviewsOmniboxUIFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("previews_verbose_iph_triggered_2",
Comparator(LESS_THAN, 2), 90, 360);
config.used = EventConfig("previews_verbose_status_opened",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(EventConfig(
"preview_page_load", Comparator(GREATER_THAN_OR_EQUAL, 1), 90, 360));
return config;
}
if (kIPHDownloadHomeFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("download_home_iph_trigger", Comparator(EQUAL, 0), 90, 360);
config.used =
EventConfig("download_home_opened", Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(EventConfig(
"download_completed", Comparator(GREATER_THAN_OR_EQUAL, 1), 90, 360));
config.snooze_params.snooze_interval = 7;
config.snooze_params.max_limit = 3;
return config;
}
if (kIPHDownloadIndicatorFeature.name == feature->name) {
// A config that allows the DownloadIndicator IPH to be shown up to 2 times,
// but only if download home hasn't been opened in the last 90 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("download_indicator_iph_trigger",
Comparator(LESS_THAN, 2), 360, 360);
config.used =
EventConfig("download_home_opened", Comparator(EQUAL, 0), 90, 360);
return config;
}
if (kIPHContextualPageActionsQuietVariantFeature.name == feature->name) {
// A config that allows the contextual page action IPH to be shown:
// * Once per day. 3 times max in 90 days
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("contextual_page_actions_quiet_variant_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360);
config.used = EventConfig("contextual_page_actions_quiet_variant_used",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("contextual_page_actions_quiet_variant_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360));
return config;
}
if (kIPHContextualPageActionsActionChipFeature.name == feature->name) {
// A config that allows the Contextual Page Action Chip to be shown:
// * 3 times per session.
// * 5 times per day.
// * 10 times per week.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 3);
config.trigger =
EventConfig("contextual_page_actions_action_chip_iph_trigger",
Comparator(LESS_THAN, 5), 1, 360);
config.event_configs.insert(
EventConfig("contextual_page_actions_action_chip_iph_trigger",
Comparator(LESS_THAN, 10), 7, 360));
return config;
}
if (kIPHAdaptiveButtonInTopToolbarCustomizationPageSummaryWebFeature.name ==
feature->name) {
// A config that allows the web page summary toolbar button IPH to be shown:
// * Once per day. 3 times max in 90 days
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("adaptive_toolbar_page_summary_web_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360);
config.used = EventConfig("adaptive_toolbar_page_summary_web_used",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("adaptive_toolbar_page_summary_web_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360));
return config;
}
if (kIPHAdaptiveButtonInTopToolbarCustomizationPageSummaryPdfFeature.name ==
feature->name) {
// A config that allows the pdf page summary toolbar button IPH to be shown:
// * Once per day. 3 times max in 90 days
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("adaptive_toolbar_page_summary_pdf_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360);
config.used = EventConfig("adaptive_toolbar_page_summary_pdf_used",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("adaptive_toolbar_page_summary_pdf_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360));
return config;
}
if (kIPHMenuAddToGroup.name == feature->name) {
// Allows an IPH for the main app menu 'Add to Group' entry:
// * Only once per year.
// * If the user has used chrome for more than 14 days.
// * And only if the menu option hasn't been opened in that time.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("menu_add_to_group_iph_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("menu_add_to_group_clicked", Comparator(EQUAL, 0),
360, 360);
return config;
}
if (kIPHPageSummaryWebMenuFeature.name == feature->name) {
// A config that allows the web page summary menu item IPH to be shown:
// * Once per day. 3 times max in 90 days
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("menu_item_page_summary_web_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360);
config.used = EventConfig("menu_item_page_summary_web_used",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("menu_item_page_summary_web_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360));
return config;
}
if (kIPHPageSummaryPdfMenuFeature.name == feature->name) {
// A config that allows the pdf page summary menu item IPH to be shown:
// * Once per day. 3 times max in 90 days
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("menu_item_page_summary_pdf_iph_trigger",
Comparator(LESS_THAN, 1), 1, 360);
config.used = EventConfig("menu_item_page_summary_pdf_used",
Comparator(EQUAL, 0), 90, 360);
config.event_configs.insert(
EventConfig("menu_item_page_summary_pdf_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360));
return config;
}
// A generic feature that always returns true.
if (kIPHGenericAlwaysTriggerHelpUiFeature.name == feature->name) {
return CreateAlwaysTriggerConfig(feature);
}
if (kIPHLowUserEngagementDetectorFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
config.trigger = EventConfig("low_user_engagement_detector_trigger",
Comparator(ANY, 0), 90, 90);
config.used = EventConfig("low_user_engagement_detector_used",
Comparator(ANY, 0), 90, 90);
config.event_configs.insert(EventConfig("foreground_session_destroyed",
Comparator(LESS_THAN_OR_EQUAL, 3),
14, 14));
return config;
}
if (kIPHFeedHeaderMenuFeature.name == feature->name) {
// A config that allows the feed header menu IPH to be shown only once when
// the user starts using a version of the feed that uploads click and view
// actions.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
// Keep the IPH trigger event for 10 years, which is a relatively long time
// period that we could consider as being "forever".
config.trigger =
EventConfig("iph_feed_header_menu_triggered", Comparator(LESS_THAN, 1),
k10YearsInDays, k10YearsInDays);
// Set a dummy config for the used event to be consistent with the other
// IPH configurations. The used event is never recorded by the feature code
// because the trigger event is already reported the first time the feed is
// being used, which corresponds to a used event.
config.used = EventConfig("iph_feed_header_menu_used", Comparator(EQUAL, 0),
k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHWebFeedAwarenessFeature.name == feature->name) {
// A config that allows the web feed IPH to be shown up to three times
// total, no more than once per session.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::ALL;
config.session_rate_impact = session_rate_impact;
// Keep the IPH trigger event for 10 years, which is a relatively long time
// period that we could consider as being "forever".
config.trigger =
EventConfig("iph_web_feed_awareness_triggered",
Comparator(LESS_THAN, 3), k10YearsInDays, k10YearsInDays);
config.used = EventConfig("web_feed_awareness_used", Comparator(ANY, 0),
k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHFeedSwipeRefresh.name == feature->name) {
// A config that allows the feed swipe refresh message IPH to be shown:
// * Once per 15 days
// * Up to 2 times but only if unused in the last 15 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("feed_swipe_refresh_iph_trigger",
Comparator(LESS_THAN, 2), 90, 90);
config.used =
EventConfig("feed_swipe_refresh_shown", Comparator(EQUAL, 0), 90, 90);
config.event_configs.insert(EventConfig("feed_swipe_refresh_iph_trigger",
Comparator(EQUAL, 0), 15, 90));
return config;
}
if (kIPHShoppingListMenuItemFeature.name == feature->name) {
// Allows a shopping list menu item IPH to be displayed at most:
// * Once per week.
// * Up to 3 times per year.
// * And only as long as the user has never initiated price tracking from
// the menu.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 1);
config.trigger = EventConfig("shopping_list_menu_item_iph_triggered",
Comparator(EQUAL, 0), 7, 7);
config.event_configs.insert(
EventConfig("shopping_list_menu_item_iph_triggered",
Comparator(LESS_THAN, 3), 360, 360));
config.used = EventConfig("shopping_list_track_price_from_menu",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabGroupsDragAndDropFeature.name == feature->name) {
// IPH for drag & drop promo in the Tab Switcher.
// * Drag & drop has not been used previously in the last 360 days.
// * Iff IPH has been shown 0 times in 30 days AND less than 2 times in 90
// days window.
// * Once per session.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("iph_tabgroups_drag_and_drop",
Comparator(EQUAL, 0), 30, 360);
config.event_configs.insert(EventConfig("iph_tabgroups_drag_and_drop",
Comparator(LESS_THAN, 2), 90, 360));
config.used = EventConfig("tab_drag_and_drop_to_group",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabGroupsRemoteGroupFeature.name == feature->name) {
// Allows an IPH for highlighting a remote tab group when:
// * Only once per day.
// * Up to 3 times per year.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("tab_groups_remote_group_triggered",
Comparator(EQUAL, 0), 1, 1);
config.event_configs.insert(EventConfig("tab_groups_remote_group_triggered",
Comparator(LESS_THAN, 3), 360,
360));
return config;
}
if (kIPHTabGroupsSurfaceFeature.name == feature->name) {
// Allows an IPH for the tab groups surface through hub toolbar when:
// * Only once per week.
// * Up to 3 times per year.
// * And only as long as the user has never manually opened the tab groups
// surface from the hub toolbar.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger =
EventConfig("tab_groups_surface_triggered", Comparator(EQUAL, 0), 7, 7);
config.event_configs.insert(EventConfig(
"tab_groups_surface_triggered", Comparator(LESS_THAN, 3), 360, 360));
config.used = EventConfig("tab_groups_surface_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabGroupsSurfaceOnHideFeature.name == feature->name) {
// Allows an IPH for the tab groups surface when hiding a tab group when:
// * Only once per year.
// * And only as long as the user has never manually opened the tab groups
// surface from the hub toolbar.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("tab_groups_surface_on_hide_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("tab_groups_surface_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabSwitcherAddToGroup.name == feature->name) {
// Allows an IPH for the 'Add to Group' 3-dot menu entry:
// * Only once per year.
// * If the user has used chrome for more than 14 days.
// * And only if the menu option hasn't been opened in that time.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("tab_switcher_add_to_group_iph_triggered",
Comparator(EQUAL, 0), 360, 360);
config.used = EventConfig("tab_switcher_add_to_group_clicked",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHTabSwitcherButtonFeature.name == feature->name) {
// Adjusted to be less spammy for users that may know what the tab switcher
// is. Show after 14 days of Chrome being installed, once every 90 days,
// unless the user has used the tab switcher button in the last year.
// Hopefully a year will be long enough that infrequent users of a given
// Chrome channel should almost never see it.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger =
EventConfig("tab_switcher_iph_triggered", Comparator(EQUAL, 0), 90, 90);
config.used = EventConfig("tab_switcher_button_clicked",
Comparator(EQUAL, 0), 360, 360);
config.snooze_params.snooze_interval = 7;
config.snooze_params.max_limit = 3;
return config;
}
if (kIPHTabSwitcherButtonSwitchIncognitoFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("tab_switcher_switch_incognito_iph_triggered",
Comparator(EQUAL, 0), 90, 90);
config.used = EventConfig("tab_switcher_button_long_clicked",
Comparator(EQUAL, 0), 14, 90);
config.snooze_params.snooze_interval = 7;
config.snooze_params.max_limit = 3;
return config;
}
if (kIPHWebFeedFollowFeature.name == feature->name) {
// A config that allows the WebFeed follow intro to be shown up to 5x per
// week.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("web_feed_follow_intro_trigger",
Comparator(LESS_THAN, 5), 7, 360);
config.used = EventConfig("web_feed_follow_intro_clicked",
Comparator(ANY, 0), 360, 360);
return config;
}
if (kIPHWebFeedPostFollowDialogFeature.name == feature->name) {
// A config that allows one of the WebFeed post follow dialogs to be
// presented 3 times.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("web_feed_post_follow_dialog_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.used = EventConfig("web_feed_post_follow_dialog_shown",
Comparator(ANY, 0), 360, 360);
return config;
}
if (kIPHWebFeedPostFollowDialogFeatureWithUIUpdate.name == feature->name) {
// A config that allows one of the WebFeed post follow dialogs to be
// presented 3 times after the UI update.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("web_feed_post_follow_dialog_trigger_with_ui_update",
Comparator(LESS_THAN, 3), 360, 360);
config.used =
EventConfig("web_feed_post_follow_dialog_shown_with_ui_update",
Comparator(ANY, 0), 360, 360);
return config;
}
if (kIPHVideoTutorialNTPChromeIntroFeature.name == feature->name) {
// A config that allows the chrome intro video tutorial card to show up
// until explicitly interacted upon.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("tutorial_chrome_intro_iph_trigger",
Comparator(ANY, 0), 90, 90);
config.used = EventConfig("chrome_intro", Comparator(ANY, 0), 90, 90);
config.event_configs.insert(
EventConfig("video_tutorial_iph_clicked_chrome_intro",
Comparator(EQUAL, 0), 90, 90));
config.event_configs.insert(
EventConfig("video_tutorial_iph_dismissed_chrome_intro",
Comparator(EQUAL, 0), 90, 90));
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHVideoTutorialNTPDownloadFeature.name == feature->name) {
// A config that allows the download video tutorial card to show up
// until explicitly interacted upon.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("tutorial_download_iph_trigger",
Comparator(ANY, 0), 90, 90);
config.used = EventConfig("download", Comparator(ANY, 0), 90, 90);
config.event_configs.insert(EventConfig(
"video_tutorial_iph_clicked_download", Comparator(EQUAL, 0), 90, 90));
config.event_configs.insert(EventConfig(
"video_tutorial_iph_dismissed_download", Comparator(EQUAL, 0), 90, 90));
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHVideoTutorialNTPSearchFeature.name == feature->name) {
// A config that allows the search video tutorial card to show up
// until explicitly interacted upon.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("tutorial_search_iph_trigger", Comparator(ANY, 0), 90, 90);
config.used = EventConfig("search", Comparator(ANY, 0), 90, 90);
config.event_configs.insert(EventConfig("video_tutorial_iph_clicked_search",
Comparator(EQUAL, 0), 90, 90));
config.event_configs.insert(EventConfig(
"video_tutorial_iph_dismissed_search", Comparator(EQUAL, 0), 90, 90));
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHVideoTutorialNTPVoiceSearchFeature.name == feature->name) {
// A config that allows the voice search video tutorial card to show up
// until explicitly interacted upon.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("tutorial_voice_search_iph_trigger",
Comparator(ANY, 0), 90, 90);
config.used = EventConfig("voice_search", Comparator(ANY, 0), 90, 90);
config.event_configs.insert(
EventConfig("video_tutorial_iph_clicked_voice_search",
Comparator(EQUAL, 0), 90, 90));
config.event_configs.insert(
EventConfig("video_tutorial_iph_dismissed_voice_search",
Comparator(EQUAL, 0), 90, 90));
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHVideoTutorialNTPSummaryFeature.name == feature->name) {
// A config that allows the summary video tutorial card to show up
// until explicitly interacted upon.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("tutorial_summary_iph_trigger", Comparator(ANY, 0), 90, 90);
config.used = EventConfig("summary", Comparator(ANY, 0), 90, 90);
config.event_configs.insert(EventConfig(
"video_tutorial_iph_dismissed_summary", Comparator(EQUAL, 0), 90, 90));
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHVideoTutorialTryNowFeature.name == feature->name) {
// A config that allows the video tutorials Try Now button click to result
// in an IPH bubble. This IPH is shown always regardless of session rate or
// any other conditions.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("tutorial_try_now_iph_trigger", Comparator(ANY, 0), 90, 90);
config.used = EventConfig("try_now", Comparator(ANY, 0), 90, 90);
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::NONE;
config.session_rate_impact = session_rate_impact;
return config;
}
if (kIPHSharedHighlightingReceiverFeature.name == feature->name) {
// A config that allows the shared highlighting message IPH to be shown
// when a user receives a highlight:
// * Once per 7 days
// * Up to 5 times but only if unused in the last 7 days.
// * Used fewer than 2 times
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("iph_shared_highlighting_receiver_trigger",
Comparator(LESS_THAN, 5), 360, 360);
config.used = EventConfig("iph_shared_highlighting_used",
Comparator(LESS_THAN, 2), 360, 360);
config.event_configs.insert(
EventConfig("iph_shared_highlighting_receiver_trigger",
Comparator(EQUAL, 0), 7, 360));
return config;
}
if (kIPHSharingHubWebnotesStylizeFeature.name == feature->name) {
// A config that allows the Webnotes Stylize IPH to be shown up to 6 times,
// but only if the feature home hasn't been used in the last 360 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("sharing_hub_webnotes_stylize_iph_trigger",
Comparator(LESS_THAN, 6), 360, 360);
config.used = EventConfig("sharing_hub_webnotes_stylize_used",
Comparator(EQUAL, 0), 360, 360);
return config;
}
if (kIPHAutoDarkOptOutFeature.name == feature->name) {
// A config that allows the auto dark dialog to be shown when a user
// disables the feature for a site in the app menu when:
// * They have not yet opened auto dark settings.
// * The dialog has been shown 0 times before.
// * They have done so at least 3 times.
// TODO(crbug.com/40198496): Update this config from test values; Will
// likely depend on giving feedback instead of opening settings, since the
// primary purpose of the dialog has changed.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used =
EventConfig("auto_dark_settings_opened", Comparator(EQUAL, 0), 90, 90);
config.trigger = EventConfig("auto_dark_opt_out_iph_trigger",
Comparator(EQUAL, 0), 90, 90);
config.event_configs.insert(
EventConfig("auto_dark_disabled_in_app_menu",
Comparator(GREATER_THAN_OR_EQUAL, 3), 90, 90));
return config;
}
if (kIPHAutoDarkUserEducationMessageFeature.name == feature->name) {
// A config that allows the auto dark message to be shown:
// * Until the user opens auto dark settings
// * 2 times per week
// * Up to 6 times (3 weeks)
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used = EventConfig("auto_dark_settings_opened", Comparator(EQUAL, 0),
360, 360);
config.trigger = EventConfig("auto_dark_user_education_message_trigger",
Comparator(LESS_THAN, 2), 7, 360);
config.event_configs.insert(
EventConfig("auto_dark_user_education_message_trigger",
Comparator(LESS_THAN, 6), 360, 360));
return config;
}
if (kIPHAutoDarkUserEducationMessageOptInFeature.name == feature->name) {
// A config that allows the auto dark message to be shown:
// * Until the user opens auto dark settings
// * 2 times per week
// * Up to 6 times (3 weeks)
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used = EventConfig("auto_dark_settings_opened", Comparator(EQUAL, 0),
360, 360);
config.trigger =
EventConfig("auto_dark_user_education_message_opt_in_trigger",
Comparator(LESS_THAN, 2), 7, 360);
config.event_configs.insert(
EventConfig("auto_dark_user_education_message_opt_in_trigger",
Comparator(LESS_THAN, 6), 360, 360));
return config;
}
if (kIPHInstanceSwitcherFeature.name == feature->name) {
// A config that allows the 'Manage windows' text bubble IPH to be shown
// only once when the user starts using the multi-instance feature by
// opening more than one window.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("instance_switcher_iph_trigger", Comparator(LESS_THAN, 1),
k10YearsInDays, k10YearsInDays);
config.used = EventConfig("instance_switcher_used", Comparator(EQUAL, 0),
k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHReadLaterAppMenuBookmarkThisPageFeature.name == feature->name) {
// A config that allows the reading list IPH bubble to prompt the user to
// bookmark this page.
// This will only occur once every 60 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("read_later_app_menu_bookmark_this_page_iph_trigger",
Comparator(EQUAL, 0), 60, 60);
config.used = EventConfig("app_menu_bookmark_star_icon_pressed",
Comparator(EQUAL, 0), 60, 60);
return config;
}
if (kIPHReadLaterAppMenuBookmarksFeature.name == feature->name) {
// A config that allows the reading list IPH bubble to promopt the user to
// open the reading list.
// This will only occur once every 60 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("read_later_app_menu_bookmarks_iph_trigger",
Comparator(EQUAL, 0), 60, 60);
config.used = EventConfig("read_later_bookmark_folder_opened",
Comparator(EQUAL, 0), 60, 60);
config.event_configs.insert(
EventConfig("read_later_article_saved",
Comparator(GREATER_THAN_OR_EQUAL, 1), 60, 60));
return config;
}
if (kIPHReadLaterContextMenuFeature.name == feature->name) {
// A config that allows the reading list label on the context menu to show
// when the context menu "copy" option is clicked.
// This will only occur once every 60 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("read_later_context_menu_tapped_iph_trigger",
Comparator(EQUAL, 0), 60, 60);
config.used = EventConfig("read_later_context_menu_tapped",
Comparator(EQUAL, 0), 60, 60);
return config;
}
if (kIPHRequestDesktopSiteDefaultOnFeature.name == feature->name) {
// A config that allows the RDS default-on message to be shown:
// * If the user has never accepted the message.
// * The message can show twice, but only once in a week.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN, 1);
config.session_rate = Comparator(LESS_THAN, 1);
config.used = EventConfig("desktop_site_settings_page_opened",
Comparator(ANY, 0), 360, 360);
config.trigger = EventConfig("request_desktop_site_default_on_iph_trigger",
Comparator(LESS_THAN_OR_EQUAL, 1), 360, 360);
config.event_configs.insert(
EventConfig("request_desktop_site_default_on_iph_trigger",
Comparator(EQUAL, 0), 7, 360));
config.event_configs.insert(
EventConfig("desktop_site_default_on_primary_action",
Comparator(EQUAL, 0), 360, 360));
config.event_configs.insert(EventConfig("desktop_site_default_on_gesture",
Comparator(ANY, 0), 360, 360));
return config;
}
if (kIPHRequestDesktopSiteExceptionsGenericFeature.name == feature->name) {
// A config that allows the RDS site-level setting IPH to be shown to
// tablet users. This will be triggered a maximum of 2 times (once per
// 2 weeks), and if the user has not used the app menu to create a desktop
// site exception in a span of a year.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 2);
config.session_rate = Comparator(LESS_THAN, 1);
config.used = EventConfig("app_menu_desktop_site_exception_added",
Comparator(EQUAL, 0), 360, 360);
config.trigger =
EventConfig("request_desktop_site_exceptions_generic_iph_trigger",
Comparator(LESS_THAN, 2), 720, 720);
config.event_configs.insert(
EventConfig("request_desktop_site_exceptions_generic_iph_trigger",
Comparator(EQUAL, 0), 14, 14));
return config;
}
if (kIPHPageZoomFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("page_zoom_iph_trigger", Comparator(EQUAL, 0), 1440, 1440);
config.used =
EventConfig("page_zoom_opened", Comparator(EQUAL, 0), 1440, 1440);
return config;
}
if (kIPHPdfPageDownloadFeature.name == feature->name) {
// A config that allows the pdf page download IPH to be shown to users.
// This will be triggered a maximum of 3 times (once per 2 weeks) with pdf
// opened, and if the user has not used the app menu to download pdf in a
// span of a year.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.used = EventConfig("app_menu_pdf_page_downloaded",
Comparator(EQUAL, 0), 360, 360);
config.trigger = EventConfig("pdf_page_download_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360);
config.event_configs.insert(EventConfig("pdf_page_download_iph_trigger",
Comparator(EQUAL, 0), 14, 14));
return config;
}
if (kIPHRestoreTabsOnFREFeature.name == feature->name) {
// A config that allows the restore tabs on FRE promo to be shown:
// * If the user has gone through the FRE workflow.
// * If the promo has never been accepted.
// * Once per week if continually dismissed for a max of 2 weeks.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(LESS_THAN_OR_EQUAL, 14);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("restore_tabs_promo_trigger", Comparator(EQUAL, 0), 7, 14);
config.used =
EventConfig("restore_tabs_promo_used", Comparator(EQUAL, 0), 14, 14);
config.event_configs.insert(
EventConfig("restore_tabs_on_first_run_show_promo",
Comparator(GREATER_THAN_OR_EQUAL, 1), 14, 14));
return config;
}
if (kIPHRequestDesktopSiteWindowSettingFeature.name == feature->name) {
// A config that allows the RDS window setting IPH to be shown at most once
// in 3 years per device.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.used = EventConfig("request_desktop_site_window_setting_iph_shown",
Comparator(EQUAL, 0), 1080, 1080);
config.trigger =
EventConfig("request_desktop_site_window_setting_iph_trigger",
Comparator(EQUAL, 0), 1080, 1080);
return config;
}
if (kIPHReadAloudExpandedPlayerFeature.name == feature->name) {
// Show tooltip at most 3 times, once a day, but stop if user saw
// expanded player.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.used = EventConfig("read_aloud_expanded_player_shown",
Comparator(EQUAL, 0), 360, 360);
config.trigger = EventConfig("read_aloud_expanded_player_shown_iph_trigger",
Comparator(EQUAL, 0), 1, 1);
config.event_configs.insert(
EventConfig("read_aloud_expanded_player_shown_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360));
return config;
}
if (kIPHReadAloudPlaybackModeFeature.name == feature->name) {
// Show tooltip at most 3 times, once a day, but stop if user hit
// playback mode.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.used = EventConfig("read_aloud_playback_mode_clicked",
Comparator(EQUAL, 0), 360, 360);
config.trigger = EventConfig("read_aloud_playback_mode_iph_trigger",
Comparator(EQUAL, 0), 1, 1);
config.event_configs.insert(
EventConfig("read_aloud_playback_mode_iph_trigger",
Comparator(LESS_THAN, 3), 360, 360));
return config;
}
if (kIPHAutofillDisabledVirtualCardSuggestionFeature.name == feature->name) {
// A config that allows the virtual card disabled suggestion IPH to be shown
// when it has been shown less than three times in last 90 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger = EventConfig("autofill_disabled_virtual_card_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360);
// This promo blocks specific promos in the same session.
config.session_rate_impact.type = SessionRateImpact::Type::EXPLICIT;
config.session_rate_impact.affected_features.emplace();
config.session_rate_impact.affected_features->push_back(
"IPH_AutofillVirtualCardSuggestion");
config.session_rate_impact.affected_features->push_back(
"IPH_KeyboardAccessoryBarSwiping");
return config;
}
if (kIPHDefaultBrowserPromoMagicStackFeature.name == feature->name) {
// A config that allows the default browser promo Magic Stack to be shown:
// * Up to 3 times
// * Neither Magic Stack, Messages, nor the settings card has been triggered
// in the last 7 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger =
EventConfig("default_browser_promo_magic_stack_trigger",
Comparator(LESS_THAN, 3), k10YearsInDays, k10YearsInDays);
config.groups.push_back(kClankDefaultBrowserPromosGroup.name);
return config;
}
if (kIPHDefaultBrowserPromoMessagesFeature.name == feature->name) {
// A config that allows the default browser promo messages to be shown:
// * Up to 2 times
// * Neither Magic Stack, Messages, nor the settings card has been triggered
// in the last 7 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger =
EventConfig("default_browser_promo_messages_trigger",
Comparator(LESS_THAN, 2), k10YearsInDays, k10YearsInDays);
config.used = EventConfig("default_browser_promo_messages_used",
Comparator(ANY, 0), 90, 90);
config.event_configs.insert(
EventConfig("default_browser_promo_messages_dismissed",
Comparator(EQUAL, 0), k10YearsInDays, k10YearsInDays));
config.groups.push_back(kClankDefaultBrowserPromosGroup.name);
return config;
}
if (kIPHDefaultBrowserPromoSettingCardFeature.name == feature->name) {
// A config that allows the default browser promo setting card to be shown:
// * Up to 4 times
// * Neither Magic Stack, Messages, nor the settings card has been triggered
// in the last 7 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger =
EventConfig("default_browser_promo_setting_card_trigger",
Comparator(LESS_THAN, 4), k10YearsInDays, k10YearsInDays);
config.used = EventConfig("default_browser_promo_setting_card_used",
Comparator(ANY, 0), 90, 90);
config.event_configs.insert(
EventConfig("default_browser_promo_setting_card_dismissed",
Comparator(EQUAL, 0), k10YearsInDays, k10YearsInDays));
config.groups.push_back(kClankDefaultBrowserPromosGroup.name);
return config;
}
if (kIPHAutofillCardInfoRetrievalSuggestionFeature.name == feature->name) {
// A config that allows the card info retrieval suggestion IPH to be shown
// when it has been shown less than three times in last 90 days and only
// once per session. IPH will not be shown once user has selected the
// suggestion.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("autofill_card_info_retrieval_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360);
config.used =
EventConfig("autofill_card_info_retrieval_suggestion_accepted",
Comparator(ANY, 0), 90, 360);
// This promo blocks specific promos in the same session.
config.session_rate_impact.type = SessionRateImpact::Type::EXPLICIT;
config.session_rate_impact.affected_features.emplace();
config.session_rate_impact.affected_features->push_back(
"IPH_KeyboardAccessoryBarSwiping");
config.session_rate_impact.affected_features->push_back(
"IPH_AutofillVirtualCardSuggestion");
return config;
}
if (kIPHTabSwitcherXR.name == feature->name) {
// A config that allows the XR tab switcher IPH to be shown at most 3 times.
// IPH will not be shown is user has clicked on it, otherwise it will be
// snoozed for one day.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger =
EventConfig("tab_switcher_xr_iph_trigger", Comparator(LESS_THAN, 3),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used = EventConfig("tab_switcher_xr_iph_used", Comparator(ANY, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.snooze_params.snooze_interval = 1;
config.snooze_params.max_limit = 3;
return config;
}
if (kIPHTabTearingXR.name == feature->name) {
// A config that allows XR tab tearing IPH to be shown
// at most 3 times. IPH will not be shown is user has clicked on it,
// otherwise it will be snoozed for one day.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.trigger =
EventConfig("tab_tearing_xr_iph_trigger", Comparator(LESS_THAN, 3),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used = EventConfig("tab_tearing_xr_iph_used", Comparator(ANY, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.snooze_params.snooze_interval = 1;
config.snooze_params.max_limit = 3;
return config;
}
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
if (kIPHAutofillCreditCardBenefitFeature.name == feature->name) {
// The credit card benefit IPH appears up to three times over 10 years and
// only once per session. Dismissing it stops it from showing again.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("autofill_credit_card_benefit_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360);
config.used =
EventConfig("autofill_credit_card_benefit_iph_accepted",
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHAutofillExternalAccountProfileSuggestionFeature.name ==
feature->name) {
// Externally created account profile suggestion IPH is shown:
// * once for an installation, 10-year window is used as the maximum
// * if there was no address keyboard accessory IPH in the last 2 weeks
// * if such a suggestion was not already accepted
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("autofill_external_account_profile_suggestion_iph_trigger",
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used =
EventConfig("autofill_external_account_profile_suggestion_accepted",
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
#if BUILDFLAG(IS_ANDROID)
config.event_configs.insert(
EventConfig("keyboard_accessory_address_filling_iph_trigger",
Comparator(EQUAL, 0), 14, k10YearsInDays));
#endif // BUILDFLAG(IS_ANDROID)
return config;
}
if (kIPHAutofillVirtualCardSuggestionFeature.name == feature->name) {
// A config that allows the virtual card credit card suggestion IPH to be
// shown when:
// * it has been shown less than three times in last 90 days;
// * the virtual card suggestion has been selected less than twice in last
// 90 days.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("autofill_virtual_card_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360);
config.used = EventConfig("autofill_virtual_card_suggestion_accepted",
Comparator(LESS_THAN, 2), 90, 360);
#if BUILDFLAG(IS_ANDROID)
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::EXPLICIT;
std::vector<std::string> affected_features;
affected_features.push_back("IPH_KeyboardAccessoryBarSwiping");
session_rate_impact.affected_features = affected_features;
config.session_rate_impact = session_rate_impact;
#endif // BUILDFLAG(IS_ANDROID)
return config;
}
if (kIPHAutofillVirtualCardCVCSuggestionFeature.name == feature->name) {
// A config that allows the virtual card CVC suggestion IPH to be
// shown when:
// * it has been shown less than three times in last 90 days;
// * the virtual card CVC suggestion has been selected less than twice in
// last 90 days.
FeatureConfig config;
config.valid = true;
#if BUILDFLAG(IS_ANDROID)
config.session_rate = Comparator(EQUAL, 0);
#else
// On desktop, toasts should always be available.
config.availability = Comparator(ANY, 0);
#endif
config.trigger = EventConfig("autofill_virtual_card_cvc_iph_trigger",
Comparator(LESS_THAN, 3), 90, 360);
config.used = EventConfig("autofill_virtual_card_cvc_suggestion_accepted",
Comparator(LESS_THAN, 2), 90, 360);
// This promo blocks specific promos in the same session.
config.session_rate_impact.type = SessionRateImpact::Type::EXPLICIT;
config.session_rate_impact.affected_features.emplace();
config.session_rate_impact.affected_features->push_back(
"IPH_AutofillVirtualCardSuggestion");
#if BUILDFLAG(IS_ANDROID)
config.session_rate_impact.affected_features->push_back(
"IPH_KeyboardAccessoryBarSwiping");
#endif // BUILDFLAG(IS_ANDROID)
return config;
}
if (kIPHCookieControlsFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// Show promo up to 3 times per year and only if user hasn't interacted with
// the cookie controls bubble in the last week.
config.trigger = EventConfig("iph_cookie_controls_triggered",
Comparator(LESS_THAN, 3), 360, 360);
#if !BUILDFLAG(IS_ANDROID)
config.used =
EventConfig(feature_engagement::events::kCookieControlsBubbleShown,
Comparator(EQUAL, 0), 7, 7);
#endif // !BUILDFLAG(IS_ANDROID)
return config;
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(IS_FUCHSIA)
#if BUILDFLAG(IS_IOS)
if (kIPHiOSLensOverlayEntrypointTipFeature.name == feature->name) {
// A config that allows the Lens overlay IPH to be shown to users. This will
// be triggered a maximum of 2 times (once per week), and if the user has
// not used lens overlay.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
constexpr char kLensOverlayFeatureTriggerEvent[] =
"lens_overlay_feature_trigger";
config.trigger =
EventConfig(kLensOverlayFeatureTriggerEvent, Comparator(LESS_THAN, 2),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.event_configs.emplace(kLensOverlayFeatureTriggerEvent,
Comparator(EQUAL, 0), 7, 7);
config.used =
EventConfig(feature_engagement::events::kLensOverlayEntrypointUsed,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHiOSLensOverlayEscapeHatchTipFeature.name == feature->name) {
// A config that allows the Lens overlay escape hatch IPH to be shown to
// users. This will be triggered a maximum of 2 times.
std::optional<FeatureConfig> config = FeatureConfig();
config->valid = true;
config->availability = Comparator(ANY, 0);
config->session_rate = Comparator(LESS_THAN, 1);
constexpr char kLensOverlayEscapeHatchFeatureTriggerEvent[] =
"lens_overlay_escape_hatch_feature_trigger";
config->trigger = EventConfig(kLensOverlayEscapeHatchFeatureTriggerEvent,
Comparator(LESS_THAN, 2),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHiOSContextualPanelPriceInsightsFeature.name == feature->name) {
// The contextual panel's price insights entrypoint IPH config to control
// the impressions of the IPH for this infoblock. Shows the IPH 3 times
// every 6 months (max 1 per day), for a maximum of 6 times lifetime. Stops
// showing the IPH if the entrypoint was used, or explicitly dismissed
// twice.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used = EventConfig(
feature_engagement::events::
kIOSContextualPanelPriceInsightsEntrypointUsed,
Comparator(LESS_THAN, 1), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used = EventConfig(
feature_engagement::events::
kIOSContextualPanelPriceInsightsEntrypointExplicitlyDismissed,
Comparator(LESS_THAN, 2), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.trigger = EventConfig(
"ios_contextual_panel_price_insights_entrypoint_iph_trigger",
Comparator(LESS_THAN, 3), 182, feature_engagement::kMaxStoragePeriod);
config.event_configs.insert(EventConfig(
"ios_contextual_panel_price_insights_entrypoint_iph_trigger",
Comparator(LESS_THAN, 1), 1, feature_engagement::kMaxStoragePeriod));
config.event_configs.insert(EventConfig(
"ios_contextual_panel_price_insights_entrypoint_iph_trigger",
Comparator(LESS_THAN, 6), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod));
// This IPH is blocked by the overflow menu's price tracking IPH
// (kIPHPriceNotificationsWhileBrowsingFeature) if shown in the same
// session (approximated by checking for an event with a 1 day lookback
// window). Done through an event config since session rate is ignored.
config.event_configs.insert(EventConfig("price_notifications_trigger",
Comparator(LESS_THAN, 1), 1, 1));
// This IPH blocks the overflow menu's price tracking IPH
// (kIPHPriceNotificationsWhileBrowsingFeature) if shown in the same
// session.
SessionRateImpact session_rate_impact;
session_rate_impact.type = SessionRateImpact::Type::EXPLICIT;
std::vector<std::string> affected_features;
affected_features.push_back("IPH_PriceNotificationsWhileBrowsing");
session_rate_impact.affected_features = affected_features;
config.session_rate_impact = session_rate_impact;
return config;
}
if (kIPHiOSContextualPanelSampleModelFeature.name == feature->name) {
// The contextual panel's sample model entrypoint IPH config to control the
// impressions of the IPH for this infoblock. Shows the IPH up to 3 times
// per day if the user doesn't interact with the entrypoint or explicitly
// dismiss it, and is blocking/blocked to/by all other IPHs.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.used = EventConfig(feature_engagement::events::
kIOSContextualPanelSampleModelEntrypointUsed,
Comparator(LESS_THAN, 1), 1, 1);
config.trigger =
EventConfig("ios_contextual_panel_sample_model_entrypoint_iph_trigger",
Comparator(LESS_THAN, 3), 1, 1);
config.event_configs.insert(EventConfig(
"ios_contextual_panel_sample_model_entrypoint_explicitly_dismissed",
Comparator(LESS_THAN, 1), 1, 1));
return config;
}
if (kIPHDefaultSiteViewFeature.name == feature->name) {
// A config that shows an IPH on the overflow menu button advertising the
// Default Page Mode feature when the user has requested the Desktop version
// of a website 3 times in 60 days. It will be shown every other year unless
// the user interacted with the setting in the past 2 years.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used =
EventConfig("default_site_view_used", Comparator(EQUAL, 0), 720, 720);
config.trigger =
EventConfig("default_site_view_shown", Comparator(EQUAL, 0), 720, 720);
config.event_configs.insert(
EventConfig("desktop_version_requested",
Comparator(GREATER_THAN_OR_EQUAL, 3), 60, 60));
return config;
}
if (kIPHWhatsNewFeature.name == feature->name) {
// A config that allows a user education bubble to be shown for the bottom
// toolbar. After the promo manager dismisses What's New promo, the user
// education bubble will be shown once. This can only occur once every a
// month.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("whats_new_trigger", Comparator(LESS_THAN, 1), 30, 360);
config.used =
EventConfig("whats_new_used", Comparator(LESS_THAN, 1), 30, 360);
return config;
}
if (kIPHPriceNotificationsWhileBrowsingFeature.name == feature->name) {
// A config that allows a user education bubble to be shown for the bottom
// toolbar. The IPH will be displayed when the user is on a page with a
// trackable product once per session for up to three sessions or until the
// user has clicked on the Price Tracking entry point. There will be a
// window of one week between impressions.
// This IPH is blocked by the Contextual Panel's price insights entrypoint
// IPH (kIPHiOSContextualPanelPriceInsightsFeature) via explicit session
// rate blocking.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("price_notifications_trigger",
Comparator(LESS_THAN, 3), 730, 730);
config.used =
EventConfig("price_notifications_used", Comparator(EQUAL, 0), 730, 730);
config.event_configs.insert(EventConfig("price_notifications_trigger",
Comparator(EQUAL, 0), 7, 730));
return config;
}
if (kIPHiOSDefaultBrowserOverflowMenuBadgeFeature.name == feature->name) {
// A config to allow a user to be shown the blue dot promo on the Chrome
// Settings icon in overflow menu and Default Browser row in Chrome
// Settings.
// This FET feature is non-blocking because it is a passive promo
// that appears alongside the rest of the UI, and does not interrupt the
// user's flow.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig("blue_dot_promo_overflow_menu_shown",
Comparator(ANY, 0), 360, 360);
// Stop showing blue dot promo if overflow menu opened while blue dot was
// showing at least 3 times.
config.used = EventConfig("blue_dot_promo_overflow_menu_opened",
Comparator(LESS_THAN, 3), 360, 360);
// Stop showing blue dot promo if default browser settings was opened at
// least once.
config.event_configs.insert(EventConfig("blue_dot_promo_settings_dismissed",
Comparator(EQUAL, 0), 360, 360));
// Stop showing blue dot promo if overflow menu was customized while blue
// dot was showing.
config.event_configs.insert(EventConfig("blue_dot_overflow_menu_customized",
Comparator(EQUAL, 0), 360, 360));
// Cooldowns from other default browser promos.
config.event_configs.insert(EventConfig("default_browser_promo_shown",
Comparator(EQUAL, 0), 14, 360));
config.event_configs.insert(EventConfig("default_browser_fre_shown",
Comparator(EQUAL, 0), 21, 360));
config.event_configs.insert(EventConfig(
"default_browser_promos_group_trigger", Comparator(EQUAL, 0), 14, 360));
config.event_configs.insert(
EventConfig(feature_engagement::events::kChromeOpened,
Comparator(GREATER_THAN_OR_EQUAL, 7), 360, 360));
// Continue checking deprecated settings badge conditions to not show blue
// dot at all if user would not have qualified for settings badge.
// TODO(crbug.com/362504599): Remove in July 2025.
config.event_configs.insert(
EventConfig("blue_dot_promo_settings_shown_new_session",
Comparator(LESS_THAN_OR_EQUAL, 2), 360, 360));
// TODO(crbug.com/362504058): Remove in Sept 2025.
config.event_configs.insert(
EventConfig("blue_dot_promo_overflow_menu_dismissed",
Comparator(LESS_THAN, 3), 360, 360));
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHiOSFeedSwipeAnimatedFeature.name == feature->name) {
// The animated IPH to promote scrolling on the feed.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(GREATER_THAN_OR_EQUAL, 3);
config.session_rate = Comparator(EQUAL, 0);
// The IPH is shown at most once.
config.trigger =
EventConfig("iph_feed_swipe_animated_trigger", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// The user hasn't scrolled on the NTP while the feed is visible.
config.used =
EventConfig(feature_engagement::events::kIOSScrolledOnFeed,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// The IPH only shows when user has engaged with the feed in any way.
config.event_configs.insert(EventConfig(
feature_engagement::events::kIOSActionOnFeed,
Comparator(GREATER_THAN, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod));
return config;
}
if (kIPHiOSFeedSwipeStaticFeature.name == feature->name) {
// The static IPH to promote scrolling on the feed.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// The IPH is shown at most once.
config.trigger =
EventConfig("iph_feed_swipe_static_trigger", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// The user hasn't scrolled on the NTP while the feed is visible.
config.used =
EventConfig(feature_engagement::events::kIOSScrolledOnFeed,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHiOSHistoryOnOverflowMenuFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
// The user hasn't tapped the history on the overflow menu.
config.used =
EventConfig(feature_engagement::events::kHistoryOnOverflowMenuUsed,
Comparator(EQUAL, 0), k10YearsInDays, k10YearsInDays);
// The IPH is shown at most 1 time a week.
config.trigger = EventConfig("history_on_overflow_menu_trigger",
Comparator(EQUAL, 0), 7, 7);
// The IPH is shown at most 2 times a year.
config.event_configs.insert(EventConfig("history_on_overflow_menu_trigger",
Comparator(LESS_THAN, 2), 365,
365));
// The user has opened URL from omnibox > 2 times in the past.
config.event_configs.insert(EventConfig(
feature_engagement::events::kOpenUrlFromOmnibox,
Comparator(GREATER_THAN, 2), k10YearsInDays, k10YearsInDays));
return config;
}
if (kIPHiOSPromoPasswordManagerWidgetFeature.name == feature->name) {
// A config to allow a user to be shown the Password Manager widget promo in
// the Password Manager. The promo will be shown for a maximum of three
// subsequent Password Manager visits to users who have not yet installed
// and used the widget. This FET feature is non-blocking because it is a
// passive promo that appears alongside the rest of the UI, and does not
// interrupt the user's flow.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger = EventConfig(
feature_engagement::events::kPasswordManagerWidgetPromoTriggered,
Comparator(LESS_THAN, 2), 360, 360);
config.used =
EventConfig(feature_engagement::events::kPasswordManagerWidgetPromoUsed,
Comparator(EQUAL, 0), 360, 360);
config.event_configs.insert(EventConfig(
feature_engagement::events::kPasswordManagerWidgetPromoClosed,
Comparator(EQUAL, 0), 360, 360));
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHiOSLensKeyboardFeature.name == feature->name) {
// A config that allows a user education bubble to be shown for the Lens
// button in the omnibox keyboard. Will be shown up to 3 times, but
// opening Lens from the keyboard will prevent the bubble from appearing
// again.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("lens_keyboard_feature_trigger", Comparator(LESS_THAN, 3),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used = EventConfig("lens_keyboard_used", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHiOSPullToRefreshFeature.name == feature->name) {
// The IPH of the pull-to-refresh feature for the current tab.
return CreateNewUserGestureInProductHelpConfig(
*feature, /*action_event=*/
feature_engagement::events::kIOSMultiGestureRefreshUsed,
/*trigger_event=*/"iph_pull_to_refresh_trigger", /*used_event=*/
feature_engagement::events::kIOSPullToRefreshUsed,
/*dismiss_button_tap_event=*/
feature_engagement::events::kIOSPullToRefreshIPHDismissButtonTapped);
}
if (kIPHiOSReminderNotificationsOverflowMenuBubbleFeature.name ==
feature->name) {
FeatureConfig config;
config.valid = true;
// No availability requirement for this feature.
config.availability = Comparator(ANY, 0);
// No session rate limit for this feature.
config.session_rate = Comparator(ANY, 0);
// Initially, show to users who haven't scheduled a tab reminder yet.
config.used =
EventConfig(feature_engagement::events::kIOSTabReminderScheduled,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// The Overflow Menu Bubble IPH should not be triggered more than 3 times in
// total.
config.trigger = EventConfig(
feature_engagement::events::
kIOSReminderNotificationsOverflowMenuBubbleIPHTrigger,
Comparator(LESS_THAN, 3), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// Space out IPH triggers to once per day.
config.event_configs.insert(EventConfig(
feature_engagement::events::
kIOSReminderNotificationsOverflowMenuBubbleIPHTrigger,
Comparator(EQUAL, 0), 1, feature_engagement::kMaxStoragePeriod));
return config;
}
if (kIPHiOSReminderNotificationsOverflowMenuNewBadgeFeature.name ==
feature->name) {
FeatureConfig config;
config.valid = true;
// No availability requirement for this feature.
config.availability = Comparator(ANY, 0);
// No session rate limit for this feature.
config.session_rate = Comparator(ANY, 0);
// Initially, show to users who haven't tapped the "Set a Reminder" overflow
// menu action yet.
config.used = EventConfig(
feature_engagement::events::kIOSOverflowMenuSetTabReminderTapped,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// The New Badge IPH should not be triggered more than 3 times
// in total.
config.trigger = EventConfig(
feature_engagement::events::
kIOSReminderNotificationsOverflowMenuNewBadgeIPHTrigger,
Comparator(LESS_THAN, 3), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// Don't show if the user has already scheduled a tab reminder.
config.event_configs.insert(
EventConfig(feature_engagement::events::kIOSTabReminderScheduled,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod));
return config;
}
if (kIPHiOSReplaceSyncPromosWithSignInPromos.name == feature->name) {
// A config to show a user education bubble from the account row in the
// settings page. Will be shown only the first time user signs-in from
// settings. Subsequent sign-ins will not trigger it.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger =
EventConfig("signin_from_settings_trigger", Comparator(LESS_THAN, 1),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used = EventConfig("signin_from_settings_used", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.blocked_by.type = BlockedBy::Type::NONE;
return config;
}
if (kIPHiOSTabGridSwipeRightForIncognito.name == feature->name) {
// The IPH of the tab grid swipe feature.
return CreateNewUserGestureInProductHelpConfig(
*feature, /*action_event=*/
feature_engagement::events::kIOSIncognitoPageControlTapped,
/*trigger_event=*/"swipe_left_for_incognito_trigger", /*used_event=*/
feature_engagement::events::
kIOSSwipeRightForIncognitoUsed, /*dismiss_button_tap_event=*/
feature_engagement::events::
kIOSSwipeRightForIncognitoIPHDismissButtonTapped);
}
if (kIPHiOSSwipeBackForwardFeature.name == feature->name) {
// The IPH of the swipe back/forward feature.
return CreateNewUserGestureInProductHelpConfig(
*feature, /*action_event=*/
feature_engagement::events::kIOSBackForwardButtonTapped,
/*trigger_event=*/"swipe_back_forward_trigger", /*used_event=*/
feature_engagement::events::kIOSSwipeBackForwardUsed,
/*dismiss_button_tap_event=*/
feature_engagement::events::kIOSSwipeBackForwardIPHDismissButtonTapped);
}
if (kIPHiOSSwipeToolbarToChangeTabFeature.name == feature->name) {
// The IPH of the swipe toolbar to go to adjacent tab feature.
return CreateNewUserGestureInProductHelpConfig(
*feature, /*action_event=*/
feature_engagement::events::kIOSTabGridAdjacentTabTapped,
/*trigger_event=*/"swipe_toolbar_to_change_tab_trigger", /*used_event=*/
feature_engagement::events::kIOSSwipeToolbarToChangeTabUsed,
/*dismiss_button_tap_event=*/
feature_engagement::events::
kIOSSwipeToolbarToChangeTabIPHDismissButtonTapped);
}
if (kIPHiOSOverflowMenuCustomizationFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.used = EventConfig(
feature_engagement::events::kIOSOverflowMenuCustomizationUsed,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.trigger =
EventConfig("overflow_menu_customization_trigger", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.event_configs.insert(EventConfig(
feature_engagement::events::kIOSOverflowMenuOffscreenItemUsed,
Comparator(GREATER_THAN_OR_EQUAL, 2),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod));
return config;
}
if (kIPHiOSPageInfoRevampFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.trigger = EventConfig("IPH_iOSPageInfoRevamp_trigger",
Comparator(LESS_THAN_OR_EQUAL, 3), 365, 365);
config.used =
EventConfig("IPH_iOSPageInfoRevamp_used", Comparator(ANY, 0), 365, 365);
return config;
}
if (kIPHiOSInlineEnhancedSafeBrowsingPromoFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(LESS_THAN, 1);
config.trigger = EventConfig("inline_enhanced_safe_browsing_promo_trigger",
Comparator(LESS_THAN_OR_EQUAL, 10), 360, 360);
config.event_configs.insert(EventConfig(
feature_engagement::events::kEnhancedSafeBrowsingPromoCriterionMet,
Comparator(GREATER_THAN_OR_EQUAL, 1), 7, 360));
config.event_configs.insert(EventConfig(
feature_engagement::events::kInlineEnhancedSafeBrowsingPromoClosed,
Comparator(EQUAL, 0), 360, 360));
config.used =
EventConfig("inline_enhanced_safe_browsing_promo_used",
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
// iOS Promo Configs are split out into a separate file, so check that too.
if (std::optional<FeatureConfig> ios_promo_feature_config =
GetClientSideiOSPromoFeatureConfig(feature)) {
return ios_promo_feature_config;
}
if (kIPHDiscoverFeedHeaderFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger = EventConfig("discover_feed_header_menu_iph_triggered",
Comparator(EQUAL, 0), 365, 365);
config.used = EventConfig("discover_feed_header_menu_clicked",
Comparator(EQUAL, 0), 365, 365);
return config;
}
if (kIPHHomeCustomizationMenuFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("home_customization_menu_iph_triggered",
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.used =
EventConfig(feature_engagement::events::kHomeCustomizationMenuUsed,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHiOSDownloadAutoDeletionFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.trigger =
EventConfig("download_auto_deletion_iph_trigger", Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.event_configs.insert(EventConfig(
feature_engagement::events::kIOSDownloadAutoDeletionIPHCriterionMet,
Comparator(GREATER_THAN_OR_EQUAL, 1), 60, 360));
config.used =
EventConfig("download_auto_deletion_iph_used", Comparator(ANY, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
return config;
}
if (kIPHiOSSettingsInOverflowMenuBubbleFeature.name == feature->name) {
// A config that allows the Settings-in-overflow-menu IPH to be shown to
// users. This will be triggered a maximum of 2 times (once per day), and it
// will stop triggering once the user opens Settings via the overflow menu.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
constexpr char kSettingsInOverflowTriggerEvent[] =
"settings_in_overflow_trigger";
// Show at most 2 times total.
config.trigger =
EventConfig(kSettingsInOverflowTriggerEvent, Comparator(LESS_THAN, 2),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
// Show at most once per day.
config.event_configs.emplace(kSettingsInOverflowTriggerEvent,
Comparator(EQUAL, 0), 1, 1);
// Stop showing once the user opens settings via the overflow menu.
config.used =
EventConfig(events::kSettingsOnOverflowMenuUsed, Comparator(EQUAL, 0),
feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kIPHAutofillHomeWorkProfileSuggestionFeature.name == feature->name) {
// Allows an IPH for showing the home and work address suggestion. This will
// only be shown once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("home_work_address_create_suggestion_feature_trigger",
Comparator(LESS_THAN, 1), k10YearsInDays, k10YearsInDays);
config.used =
EventConfig("home_work_address_create_suggestion_feature_used",
Comparator(EQUAL, 0), k10YearsInDays, k10YearsInDays);
return config;
}
if (kIPHiOSSwitchAccountsWithNTPAccountParticleDiscFeature.name ==
feature->name) {
// A config that allows the NTP-identity-disc IPH to be shown to users. This
// will only triggered once.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
constexpr char kSwitchAccountsWithAccountParticleTrigger[] =
"switch_accounts_with_account_particle_trigger";
// Show only once.
config.trigger =
EventConfig(kSwitchAccountsWithAccountParticleTrigger,
Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod);
return config;
}
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_CHROMEOS)
if (std::optional<FeatureConfig> scalable_iph_feature_config =
GetScalableIphFeatureConfig(feature)) {
return scalable_iph_feature_config;
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_CHROMEOS)
if (kIPHLauncherSearchHelpUiFeature.name == feature->name) {
// A config that allows the ChromeOS Ash Launcher search IPH to be shown.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.session_rate_impact.type = SessionRateImpact::Type::NONE;
config.blocked_by.type = BlockedBy::Type::NONE;
config.blocking.type = Blocking::Type::NONE;
// Can be shown any time until the `assistant_click` event is recorded.
config.trigger =
EventConfig("IPH_LauncherSearchHelpUi_trigger", Comparator(ANY, 0),
kMaxStoragePeriod, kMaxStoragePeriod);
config.used =
EventConfig("IPH_LauncherSearchHelpUi_chip_click", Comparator(ANY, 0),
kMaxStoragePeriod, kMaxStoragePeriod);
config.event_configs.insert(EventConfig(
"IPH_LauncherSearchHelpUi_assistant_click", Comparator(EQUAL, 0),
kMaxStoragePeriod, kMaxStoragePeriod));
return config;
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (kIPHPlusAddressCreateSuggestionFeature.name == feature->name) {
// A config that allows a user education bubble to be shown for the plus
// address feature. Will be shown up to 9 times in the 90 day window with
// the exception of 2 times if the user accepted the suggestion.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(EQUAL, 0);
config.trigger =
EventConfig("plus_address_create_suggestion_feature_trigger",
Comparator(LESS_THAN, 9), 90, 360);
config.used = EventConfig("plus_address_create_suggestion_feature_used",
Comparator(LESS_THAN, 2), 90, 360);
return config;
}
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (kIPHDummyFeature.name == feature->name) {
// Only used for tests. Various magic tricks are used below to ensure this
// config is invalid and unusable.
FeatureConfig config;
config.valid = true;
config.availability = Comparator(LESS_THAN, 0);
config.session_rate = Comparator(LESS_THAN, 0);
config.trigger = EventConfig("dummy_feature_iph_trigger",
Comparator(LESS_THAN, 0), 1, 1);
config.used =
EventConfig("dummy_feature_action", Comparator(LESS_THAN, 0), 1, 1);
return config;
}
return std::nullopt;
}
} // namespace feature_engagement