0

[iOS] Change Impression Management for Non-Modal DB promo

The Non-Modal DB promo were decoupled recently and the impression management had to reflect the new impression rules.

Change-Id: Icbe2964f6a700c5f7f195b588bb79cccf3beaaf7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6253744
Reviewed-by: Guillaume Jenkins <gujen@google.com>
Reviewed-by: Robbie Gibson <rkgibson@google.com>
Reviewed-by: Rohit Rao <rohitrao@chromium.org>
Commit-Queue: Pier-Alexandre Bouchard <pabouchard@google.com>
Cr-Commit-Position: refs/heads/main@{#1424781}
This commit is contained in:
Pier-Alexandre Bouchard
2025-02-25 13:48:21 -08:00
committed by Chromium LUCI CQ
parent 3915d8b8c6
commit 708f5704f4
22 changed files with 314 additions and 53 deletions

@ -68,6 +68,10 @@ const char kDefaultBrowserPromoRemindMeLater[] =
"default_browser_promo_remind_me_later";
const char kNonModalDefaultBrowserPromoUrlPasteTrigger[] =
"non_modal_default_browser_promo_url_paste_trigger";
const char kNonModalDefaultBrowserPromoAppSwitcherTrigger[] =
"non_modal_default_browser_promo_app_switcher_trigger";
const char kNonModalDefaultBrowserPromoShareTrigger[] =
"non_modal_default_browser_promo_share_trigger";
const char kPasswordManagerWidgetPromoTriggered[] =
"password_manager_widget_promo_trigger";
const char kPasswordManagerWidgetPromoUsed[] =

@ -117,6 +117,12 @@ extern const char kDefaultBrowserPromoRemindMeLater[];
// The non-modal default browser promo from omnibox paste was triggered.
extern const char kNonModalDefaultBrowserPromoUrlPasteTrigger[];
// The non-modal default browser promo from App switcher was triggered.
extern const char kNonModalDefaultBrowserPromoAppSwitcherTrigger[];
// The non-modal default browser promo from share was triggered.
extern const char kNonModalDefaultBrowserPromoShareTrigger[];
// The Password Manager widget promo was triggered.
extern const char kPasswordManagerWidgetPromoTriggered[];

@ -581,6 +581,12 @@ BASE_FEATURE(kIPHiOSPromoPostRestoreDefaultBrowserFeature,
BASE_FEATURE(kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature,
"IPH_iOSPromoNonModalUrlPasteDefaultBrowser",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature,
"IPH_iOSPromoNonModalAppSwitcherDefaultBrowser",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kIPHiOSPromoNonModalShareDefaultBrowserFeature,
"IPH_iOSPromoNonModalShareDefaultBrowser",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kIPHiOSPromoPasswordManagerWidgetFeature,
"IPH_iOSPromoPasswordManagerWidget",
base::FEATURE_ENABLED_BY_DEFAULT);

@ -250,6 +250,10 @@ FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSHistoryOnOverflowMenuFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSPromoPostRestoreDefaultBrowserFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(
kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(
kIPHiOSPromoNonModalShareDefaultBrowserFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSPromoPasswordManagerWidgetFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSParcelTrackingFeature);
FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSPullToRefreshFeature);

@ -140,6 +140,8 @@ const base::Feature* const kAllFeatures[] = {
&kIPHiOSHistoryOnOverflowMenuFeature,
&kIPHiOSPromoPostRestoreDefaultBrowserFeature,
&kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature,
&kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature,
&kIPHiOSPromoNonModalShareDefaultBrowserFeature,
&kIPHiOSPromoPasswordManagerWidgetFeature,
&kIPHiOSParcelTrackingFeature,
&kIPHiOSPullToRefreshFeature,

@ -249,6 +249,10 @@ DEFINE_VARIATION_PARAM(kIPHiOSPromoPostRestoreDefaultBrowserFeature,
"IPH_iOSPromoPostRestoreDefaultBrowser");
DEFINE_VARIATION_PARAM(kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature,
"IPH_iOSPromoNonModalUrlPasteDefaultBrowser");
DEFINE_VARIATION_PARAM(kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature,
"IPH_iOSPromoNonModalAppSwitcherDefaultBrowser");
DEFINE_VARIATION_PARAM(kIPHiOSPromoNonModalShareDefaultBrowserFeature,
"IPH_iOSPromoNonModalShareDefaultBrowser");
DEFINE_VARIATION_PARAM(kIPHiOSPromoPasswordManagerWidgetFeature,
"IPH_iOSPromoPasswordManagerWidget");
DEFINE_VARIATION_PARAM(kIPHiOSParcelTrackingFeature,
@ -651,6 +655,8 @@ inline constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHiOSHistoryOnOverflowMenuFeature),
VARIATION_ENTRY(kIPHiOSPromoPostRestoreDefaultBrowserFeature),
VARIATION_ENTRY(kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature),
VARIATION_ENTRY(kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature),
VARIATION_ENTRY(kIPHiOSPromoNonModalShareDefaultBrowserFeature),
VARIATION_ENTRY(kIPHiOSPromoPasswordManagerWidgetFeature),
VARIATION_ENTRY(kIPHiOSParcelTrackingFeature),
VARIATION_ENTRY(kIPHiOSPullToRefreshFeature),

@ -64,6 +64,24 @@ std::optional<GroupConfig> GetClientSideGroupConfig(
feature_engagement::kMaxStoragePeriod);
return config;
}
if (kiOSTailoredNonModalDefaultBrowserPromosGroup.name == group->name) {
std::optional<GroupConfig> config = GroupConfig();
config->valid = true;
config->session_rate = Comparator(ANY, 0);
// No more than 1 promo across all variants per day.
config->trigger =
EventConfig("tailored_non_modal_default_browser_promos_group_trigger",
Comparator(LESS_THAN, 1), 1, 365);
// No more than 4 promos across all variants per 6 months
config->event_configs.insert(
EventConfig("tailored_non_modal_default_browser_promos_group_trigger",
Comparator(LESS_THAN, 4), 180, 365));
return config;
}
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_ANDROID)

@ -26,6 +26,9 @@ BASE_FEATURE(kiOSDefaultBrowserPromosGroup,
BASE_FEATURE(kiOSTailoredDefaultBrowserPromosGroup,
"IPH_iOSTailoredDefaultBrowserPromosGroup",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kiOSTailoredNonModalDefaultBrowserPromosGroup,
"IPH_iOSTailoredNonModalDefaultBrowserPromosGroup",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_ANDROID)
BASE_FEATURE(kClankDefaultBrowserPromosGroup,

@ -17,6 +17,7 @@ BASE_DECLARE_FEATURE(kIPHDummyGroup);
BASE_DECLARE_FEATURE(kiOSFullscreenPromosGroup);
BASE_DECLARE_FEATURE(kiOSDefaultBrowserPromosGroup);
BASE_DECLARE_FEATURE(kiOSTailoredDefaultBrowserPromosGroup);
BASE_DECLARE_FEATURE(kiOSTailoredNonModalDefaultBrowserPromosGroup);
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_ANDROID)
BASE_DECLARE_FEATURE(kClankDefaultBrowserPromosGroup);

@ -18,6 +18,7 @@ const base::Feature* const kAllGroups[] = {
&kiOSFullscreenPromosGroup,
&kiOSDefaultBrowserPromosGroup,
&kiOSTailoredDefaultBrowserPromosGroup,
&kiOSTailoredNonModalDefaultBrowserPromosGroup,
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_ANDROID)
&kClankDefaultBrowserPromosGroup,

@ -315,12 +315,56 @@ std::optional<FeatureConfig> GetCustomConfig(const base::Feature* feature) {
config.trigger = EventConfig(
feature_engagement::events::kNonModalDefaultBrowserPromoUrlPasteTrigger,
Comparator(LESS_THAN, 1), 14, 365);
// The limit for promo triggers should be 10.
// Should be triggered no more than twice (2x) every 6 months.
config.event_configs.insert(EventConfig(
feature_engagement::events::kNonModalDefaultBrowserPromoUrlPasteTrigger,
Comparator(LESS_THAN, 10), feature_engagement::kMaxStoragePeriod,
feature_engagement::kMaxStoragePeriod));
Comparator(LESS_THAN, 2), 180, 365));
config.groups.push_back(kiOSTailoredNonModalDefaultBrowserPromosGroup.name);
return config;
}
if (kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature.name ==
feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used =
EventConfig("non_modal_default_browser_promo_app_switcher_used",
Comparator(ANY, 0), 365, 365);
// Should be triggered no more than once every 14 days.
config.trigger =
EventConfig(feature_engagement::events::
kNonModalDefaultBrowserPromoAppSwitcherTrigger,
Comparator(LESS_THAN, 1), 14, 365);
// Should be triggered no more than twice (2x) every 6 months.
config.event_configs.insert(
EventConfig(feature_engagement::events::
kNonModalDefaultBrowserPromoAppSwitcherTrigger,
Comparator(LESS_THAN, 2), 180, 365));
config.groups.push_back(kiOSTailoredNonModalDefaultBrowserPromosGroup.name);
return config;
}
if (kIPHiOSPromoNonModalShareDefaultBrowserFeature.name == feature->name) {
FeatureConfig config;
config.valid = true;
config.availability = Comparator(ANY, 0);
config.session_rate = Comparator(ANY, 0);
config.used = EventConfig("non_modal_default_browser_promo_share_used",
Comparator(ANY, 0), 365, 365);
// Should be triggered no more than once every 14 days.
config.trigger = EventConfig(
feature_engagement::events::kNonModalDefaultBrowserPromoShareTrigger,
Comparator(LESS_THAN, 1), 14, 365);
// Should be triggered no more than twice (2x) every 6 months.
config.event_configs.insert(EventConfig(
feature_engagement::events::kNonModalDefaultBrowserPromoShareTrigger,
Comparator(LESS_THAN, 2), 180, 365));
config.groups.push_back(kiOSTailoredNonModalDefaultBrowserPromosGroup.name);
return config;
}

@ -18,6 +18,7 @@ source_set("utils") {
"utils.mm",
]
deps = [
":features",
"//components/feature_engagement/public",
"//components/sync/service",
"//ios/chrome/browser/shared/model/application_context",

@ -81,7 +81,7 @@ enum class NonModalDefaultBrowserPromoReason {
PromoReasonOmniboxPaste = 1,
// The promo reason used when a user opens Chrome from a first-party app.
PromoReasonExternalLink = 2,
PromoReasonAppSwitcher = 2,
// The promo reason used when a user shares Chrome via the share feature.
PromoReasonShare = 3,
@ -345,6 +345,25 @@ void LogDefaultBrowserPromoHistogramForAction(
const std::string IOSDefaultBrowserPromoActionToString(
IOSDefaultBrowserPromoAction action);
// Returns the feature associated with a given promo reason.
//
// The promo reason (`promo_reason`) represents the event or condition
// that triggered a non-modal default browser promotion. This function
// maps the promo reason to its corresponding feature.
//
const base::Feature& GetFeatureForPromoReason(
NonModalDefaultBrowserPromoReason promo_reason);
// Returns the feature engagement event name associated with a given promo
// reason.
//
// The promo reason (`promo_reason`) represents the event or condition
// that triggered a non-modal default browser promotion. This function
// maps the promo reason to its corresponding feature engagement event name.
//
const std::string GetFeatureEventNameForPromoReason(
NonModalDefaultBrowserPromoReason promo_reason);
// Returns PromoStatistics object with all properties calculated.
PromoStatistics* CalculatePromoStatistics();

@ -19,6 +19,7 @@
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/prefs/pref_service.h"
#import "ios/chrome/browser/default_browser/model/features.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
@ -791,6 +792,49 @@ const std::string IOSDefaultBrowserPromoActionToString(
}
}
const base::Feature& GetFeatureForPromoReason(
NonModalDefaultBrowserPromoReason promo_reason) {
if (!IsTailoredNonModalDBPromoEnabled()) {
return feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature;
}
switch (promo_reason) {
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
return feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature;
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
return feature_engagement::
kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature;
case NonModalDefaultBrowserPromoReason::PromoReasonShare:
return feature_engagement::kIPHiOSPromoNonModalShareDefaultBrowserFeature;
case NonModalDefaultBrowserPromoReason::PromoReasonNone:
NOTREACHED();
}
}
const std::string GetFeatureEventNameForPromoReason(
NonModalDefaultBrowserPromoReason promo_reason) {
if (!IsTailoredNonModalDBPromoEnabled()) {
return feature_engagement::events::
kNonModalDefaultBrowserPromoUrlPasteTrigger;
}
switch (promo_reason) {
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
return feature_engagement::events::
kNonModalDefaultBrowserPromoUrlPasteTrigger;
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
return feature_engagement::events::
kNonModalDefaultBrowserPromoAppSwitcherTrigger;
case NonModalDefaultBrowserPromoReason::PromoReasonShare:
return feature_engagement::events::
kNonModalDefaultBrowserPromoShareTrigger;
case NonModalDefaultBrowserPromoReason::PromoReasonNone:
NOTREACHED();
}
}
void RecordPromoStatsToUMAForActionString(PromoStatistics* promo_stats,
const std::string& action_str) {
if (!IsDefaultBrowserTriggerCriteraExperimentEnabled()) {

@ -112,6 +112,7 @@ source_set("eg2_tests") {
"//components/strings",
"//ios/chrome/app/strings",
"//ios/chrome/browser/authentication/ui_bundled:eg_test_support+eg2",
"//ios/chrome/browser/default_browser/model:features",
"//ios/chrome/browser/infobars/ui_bundled/banners:public",
"//ios/chrome/browser/shared/public/features",
"//ios/chrome/browser/signin/model:fake_system_identity",

@ -136,8 +136,7 @@
feature_engagement::Tracker* tracker =
feature_engagement::TrackerFactory::GetForProfile(
self.browser->GetProfile());
tracker->Dismissed(
feature_engagement::kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature);
tracker->Dismissed(GetFeatureForPromoReason(_promoReason));
}
id<DefaultBrowserPromoNonModalCommands> handler =
@ -172,7 +171,7 @@
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
return l10n_util::GetNSString(
IDS_IOS_DEFAULT_BROWSER_NON_MODAL_OMNIBOX_NAVIGATION_DESCRIPTION);
case NonModalDefaultBrowserPromoReason::PromoReasonExternalLink:
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
return l10n_util::GetNSString(
IDS_IOS_DEFAULT_BROWSER_NON_MODAL_1P_APP_DESCRIPTION);
case NonModalDefaultBrowserPromoReason::PromoReasonShare:
@ -195,7 +194,7 @@
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
return l10n_util::GetNSString(
IDS_IOS_DEFAULT_BROWSER_NON_MODAL_OMNIBOX_NAVIGATION_TITLE);
case NonModalDefaultBrowserPromoReason::PromoReasonExternalLink:
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
return l10n_util::GetNSString(
IDS_IOS_DEFAULT_BROWSER_NON_MODAL_1P_APP_TITLE);
case NonModalDefaultBrowserPromoReason::PromoReasonShare:

@ -4,15 +4,17 @@
#import "base/functional/bind.h"
#import "base/ios/ios_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "components/feature_engagement/public/feature_constants.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/default_browser/model/features.h"
#import "ios/chrome/browser/infobars/ui_bundled/banners/infobar_banner_constants.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/chrome/test/earl_grey/test_switches.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
@ -31,8 +33,13 @@ namespace {
// slightly delayed.
constexpr base::TimeDelta kShowPromoWebpageLoadWaitTime = base::Seconds(5);
// Returns a matcher for the non modal promo title.
id<GREYMatcher> NonModalTitleMatcher() {
id<GREYMatcher> NonModalShareTitleMatcher() {
NSString* a11yLabelText =
l10n_util::GetNSString(IDS_IOS_DEFAULT_BROWSER_NON_MODAL_SHARE_TITLE);
return grey_accessibilityLabel(a11yLabelText);
}
id<GREYMatcher> NonModalPasteTitleMatcher() {
NSString* a11yLabelText = l10n_util::GetNSString(
IDS_IOS_DEFAULT_BROWSER_NON_MODAL_OMNIBOX_NAVIGATION_TITLE);
return grey_accessibilityLabel(a11yLabelText);
@ -48,14 +55,6 @@ id<GREYMatcher> NonModalTitleMatcher() {
- (void)setUp {
[super setUp];
AppLaunchConfiguration config;
config.relaunch_policy = ForceRelaunchByCleanShutdown;
config.additional_args.push_back(
std::string("-") + test_switches::kEnableIPH +
"=IPH_iOSPromoNonModalUrlPasteDefaultBrowser");
[[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
[ChromeEarlGrey clearDefaultBrowserPromoData];
}
@ -65,9 +64,59 @@ id<GREYMatcher> NonModalTitleMatcher() {
[ChromeEarlGrey clearDefaultBrowserPromoData];
}
- (AppLaunchConfiguration)appConfigurationForTestCase {
AppLaunchConfiguration config;
config.features_enabled.push_back(kTailoredNonModalDBPromo);
return config;
}
- (void)setupIPHConfig:(std::string)IPHconfigName {
AppLaunchConfiguration config = [self appConfigurationForTestCase];
config.relaunch_policy = ForceRelaunchByCleanShutdown;
config.iph_feature_enabled = IPHconfigName;
[[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
}
// Test that a non modal default modal promo appears when it is triggered by
// using a pasted URL.
- (void)testNonModalAppearsFromPaste {
[self setupIPHConfig:"IPH_iOSPromoNonModalUrlPasteDefaultBrowser"];
// Copy URL to the clipboard
[ChromeEarlGrey copyTextToPasteboard:@"google.com"];
// Access test URL
const GURL destinationUrl = self.testServer->GetURL("/destination.html");
[ChromeEarlGrey loadURL:destinationUrl];
// Paste the copied URL
[[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
performAction:grey_longPress()];
[[[[EarlGrey
selectElementWithMatcher:grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_VISIT_COPIED_LINK))] atIndex:0]
assertWithMatcher:grey_sufficientlyVisible()] performAction:grey_tap()];
// Wait until the promo appears.
NSString* description = @"Wait for the promo to appear.";
ConditionBlock condition = ^{
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:NonModalPasteTitleMatcher()]
assertWithMatcher:grey_sufficientlyVisible()
error:&error];
return (error == nil);
};
GREYAssert(
WaitUntilConditionOrTimeout(kShowPromoWebpageLoadWaitTime, condition),
description);
}
// Test that a non modal default modal promo appears when it is triggered by
// using the share menu.
- (void)testNonModalAppears {
- (void)testNonModalAppearsFromShare {
[self setupIPHConfig:"IPH_iOSPromoNonModalShareDefaultBrowser"];
const GURL destinationUrl = self.testServer->GetURL("/destination.html");
[ChromeEarlGrey loadURL:destinationUrl];
@ -83,7 +132,7 @@ id<GREYMatcher> NonModalTitleMatcher() {
NSString* description = @"Wait for the promo to appear.";
ConditionBlock condition = ^{
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:NonModalTitleMatcher()]
[[EarlGrey selectElementWithMatcher:NonModalShareTitleMatcher()]
assertWithMatcher:grey_sufficientlyVisible()
error:&error];
return (error == nil);

@ -42,6 +42,7 @@ source_set("unit_tests") {
"//ios/chrome/app:app_internal",
"//ios/chrome/app/application_delegate:app_state",
"//ios/chrome/app/application_delegate:test_support",
"//ios/chrome/browser/default_browser/model:features",
"//ios/chrome/browser/default_browser/model:test_support",
"//ios/chrome/browser/default_browser/model:utils",
"//ios/chrome/browser/default_promo/ui_bundled:coordinator",

@ -50,7 +50,7 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
return NonModalPromoTriggerType::kUnknown;
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
return NonModalPromoTriggerType::kPastedLink;
case NonModalDefaultBrowserPromoReason::PromoReasonExternalLink:
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
return NonModalPromoTriggerType::kGrowthKitOpen;
case NonModalDefaultBrowserPromoReason::PromoReasonShare:
return NonModalPromoTriggerType::kShare;
@ -136,7 +136,12 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
return;
}
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste;
if (![self promoCanBeDisplayed]) {
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonNone;
return;
}
@ -145,12 +150,11 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
web::WebState* activeWebState = self.webStateList->GetActiveWebState();
// There should always be an active web state when pasting in the omnibox.
if (!activeWebState) {
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonNone;
return;
}
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste;
// Store the pasted web state, so when that web state's page load finishes,
// the promo can be shown.
self.webStateToListenTo = activeWebState;
@ -162,11 +166,14 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
return;
}
self.currentPromoReason = NonModalDefaultBrowserPromoReason::PromoReasonShare;
if (![self promoCanBeDisplayed]) {
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonNone;
return;
}
self.currentPromoReason = NonModalDefaultBrowserPromoReason::PromoReasonShare;
[self startShowPromoTimer];
}
@ -176,13 +183,15 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
return;
}
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher;
if (![self promoCanBeDisplayed]) {
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonNone;
return;
}
self.currentPromoReason =
NonModalDefaultBrowserPromoReason::PromoReasonExternalLink;
// Store the current web state, so when that web state's page load finishes,
// the promo can be shown.
self.webStateToListenTo = self.webStateList->GetActiveWebState();
@ -226,7 +235,7 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
if (IsNonModalPromoMigrationEnabled()) {
return self.tracker->WouldTriggerHelpUI(
feature_engagement::kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature);
GetFeatureForPromoReason(self.currentPromoReason));
}
if (UserInNonModalPromoCooldown()) {
@ -245,8 +254,8 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
UserInteractionWithNonModalPromoCount();
if (!IsNonModalPromoMigrationEnabled() && IsNonModalPromoMigrationDone()) {
self.tracker->NotifyEvent(feature_engagement::events::
kNonModalDefaultBrowserPromoUrlPasteTrigger);
self.tracker->NotifyEvent(
GetFeatureEventNameForPromoReason(self.currentPromoReason));
}
[_handler showDefaultBrowserNonModalPromoWithReason:self.currentPromoReason];
@ -388,7 +397,7 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
// when the current web state is the one that was active when the link was
// opened.
if (self.currentPromoReason ==
NonModalDefaultBrowserPromoReason::PromoReasonExternalLink &&
NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher &&
self.webStateList->GetActiveWebState() == self.webStateToListenTo &&
status.active_web_state_change()) {
const WebStateListChangeInsert& insertChange =
@ -492,7 +501,7 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
case NonModalDefaultBrowserPromoReason::PromoReasonOmniboxPaste:
promoTimeInterval = kShowPromoWebpageLoadWaitTime;
break;
case NonModalDefaultBrowserPromoReason::PromoReasonExternalLink:
case NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher:
promoTimeInterval = kShowPromoWebpageLoadWaitTime;
break;
case NonModalDefaultBrowserPromoReason::PromoReasonShare:
@ -525,8 +534,7 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
if (IsNonModalPromoMigrationEnabled() &&
!self.tracker->ShouldTriggerHelpUI(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature)) {
GetFeatureForPromoReason(self.currentPromoReason))) {
return;
}
@ -571,11 +579,10 @@ NonModalPromoTriggerType MetricTypeForPromoReason(
unsigned int interactions = 0;
std::vector<std::pair<feature_engagement::EventConfig, int>> events =
self.tracker->ListEvents(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature);
GetFeatureForPromoReason(self.currentPromoReason));
for (const auto& event : events) {
if (event.first.name == feature_engagement::events::
kNonModalDefaultBrowserPromoUrlPasteTrigger) {
if (event.first.name ==
GetFeatureEventNameForPromoReason(self.currentPromoReason)) {
interactions = event.second;
break;
}

@ -16,6 +16,7 @@
#import "components/feature_engagement/test/test_tracker.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/fake_startup_information.h"
#import "ios/chrome/browser/default_browser/model/features.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/default_browser/model/utils_test_support.h"
#import "ios/chrome/browser/default_promo/ui_bundled/default_browser_promo_non_modal_commands.h"
@ -148,6 +149,8 @@ class NonModalDefaultBrowserPromoSchedulerSceneAgentTest : public PlatformTest {
// Tests that the omnibox paste event triggers the promo to show.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestOmniboxPasteShowsPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -185,16 +188,18 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// promo.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestFirstPartySchemeShowsPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature)))
kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature)))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_tracker_,
ShouldTriggerHelpUI(testing::Ref(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature)))
kIPHiOSPromoNonModalAppSwitcherDefaultBrowserFeature)))
.WillRepeatedly(testing::Return(true));
[scheduler_ logUserEnteredAppViaFirstPartyScheme];
@ -212,7 +217,7 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// trigger the promo.
[[promo_commands_handler_ expect]
showDefaultBrowserNonModalPromoWithReason:
NonModalDefaultBrowserPromoReason::PromoReasonExternalLink];
NonModalDefaultBrowserPromoReason::PromoReasonAppSwitcher];
task_env_.FastForwardBy(base::Seconds(2));
[promo_commands_handler_ verify];
@ -221,16 +226,18 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests that the completed share event triggers the promo.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestShareCompletedShowsPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature)))
EXPECT_CALL(
*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
feature_engagement::kIPHiOSPromoNonModalShareDefaultBrowserFeature)))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_tracker_,
ShouldTriggerHelpUI(testing::Ref(
feature_engagement::
kIPHiOSPromoNonModalUrlPasteDefaultBrowserFeature)))
EXPECT_CALL(
*mock_tracker_,
ShouldTriggerHelpUI(testing::Ref(
feature_engagement::kIPHiOSPromoNonModalShareDefaultBrowserFeature)))
.WillRepeatedly(testing::Return(true));
[scheduler_ logUserFinishedActivityFlow];
@ -253,6 +260,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// the event is stored.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestTimeoutDismissesPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -294,6 +303,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests that if the user takes the promo action, that is handled correctly.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestActionDismissesPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -357,6 +368,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// interactions count is only incremented once.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestMultipleInteractionsOnlyIncrementsCountOnce) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -417,6 +430,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// finishes, the promo does not show.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestTabSwitchPreventsPromoShown) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -449,6 +464,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests that if a message is triggered on page load, the promo is not shown.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestMessagePreventsPromoShown) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -495,6 +512,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// does not update the shown promo count.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundingDismissesPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -557,6 +576,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// does not update the shown promo count.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestTabGridDismissesPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -617,6 +638,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests background cancel metric logs correctly.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundCancelMetric) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -650,6 +673,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests background cancel metric is not logged after a promo is shown.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundCancelMetricNotLogAfterPromoShown) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -695,6 +720,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests background cancel metric is not logged after a promo is dismissed.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundCancelMetricNotLogAfterPromoDismiss) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -746,6 +773,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Prevents crbug.com/1221379 regression.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundCancelMetricDoesNotLogWhenPromoNotShown) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
base::HistogramTester histogram_tester;
histogram_tester.ExpectUniqueSample(
"IOS.DefaultBrowserPromo.NonModal.VisitPastedLink",
@ -795,6 +824,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// Tests that if the user currently has Chrome as default, the promo does not
// show. Prevents regression of crbug.com/1224875
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest, NoPromoIfDefault) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mark Chrome as currently default
LogOpenHTTPURLFromExternalURL();
@ -814,6 +845,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest, NoPromoIfDefault) {
// crbug.com/1224427
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
NoDCHECKIfPromoNotShown) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Mock the FET tracker.
EXPECT_CALL(*mock_tracker_,
WouldTriggerHelpUI(testing::Ref(
@ -852,6 +885,8 @@ TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
// have been displayed. See b/326565601.
TEST_F(NonModalDefaultBrowserPromoSchedulerSceneAgentTest,
TestBackgroundingDoesNotRecordIfCannotDisplayPromo) {
base::test::ScopedFeatureList feature_list(kTailoredNonModalDBPromo);
// Make sure the impression limit is met.
for (int i = 0; i < GetNonModalDefaultBrowserPromoImpressionLimit(); i++) {
LogUserInteractionWithNonModalPromo(i);

@ -47748,9 +47748,14 @@ should be able to be added at any place in this file.
label="For generivc default browser promo."/>
<suffix name="iOSPromoMadeForIOS"
label="For Made for iOS default browser promo."/>
<suffix name="iOSPromoNonModalUrlPasteDefaultBrowser"
label="howing the non-modal Default Browser Promo from omnibox paste on
<suffix name="iOSPromoNonModalAppSwitcherDefaultBrowser"
label="Showing the non-modal Default Browser Promo from app switcher on
iOS."/>
<suffix name="iOSPromoNonModalShareDefaultBrowser"
label="Showing the non-modal Default Browser Promo from share on iOS."/>
<suffix name="iOSPromoNonModalUrlPasteDefaultBrowser"
label="Showing the non-modal Default Browser Promo from omnibox paste
on iOS."/>
<suffix name="iOSPromoPasswordManagerWidget"
label="For iOSPromoPasswordManagerWidget feature"/>
<suffix name="iOSPromoPostRestore" label="For iOSPromoPostRestore feature"/>

@ -303,6 +303,11 @@ chromium-metrics-reviews@google.com.
summary="showing generic default browser promo"/>
<variant name="IPH_iOSPromoMadeForIOS"
summary="showing Made for iOS default browser promo"/>
<variant name="IPH_iOSPromoNonModalAppSwitcherDefaultBrowser"
summary="showing the non-modal Default Browser Promo from app switcher
on iOS"/>
<variant name="IPH_iOSPromoNonModalShareDefaultBrowser"
summary="showing the non-modal Default Browser Promo from share on iOS"/>
<variant name="IPH_iOSPromoNonModalUrlPasteDefaultBrowser"
summary="showing the non-modal Default Browser Promo from omnibox paste
on iOS"/>