Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/browser/banners/app_banner_settings_helper.cc

Issue 2553013004: Remove the app banner navigation heuristic. (Closed)
Patch Set: Rebase Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/banners/app_banner_settings_helper.h" 5 #include "chrome/browser/banners/app_banner_settings_helper.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <memory>
10 #include <string> 10 #include <string>
11 #include <utility> 11 #include <utility>
12 12
13 #include "base/command_line.h" 13 #include "base/command_line.h"
14 #include "base/memory/ptr_util.h" 14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "chrome/browser/banners/app_banner_manager.h" 16 #include "chrome/browser/banners/app_banner_manager.h"
19 #include "chrome/browser/banners/app_banner_metrics.h" 17 #include "chrome/browser/banners/app_banner_metrics.h"
20 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" 19 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
22 #include "chrome/browser/installable/installable_logging.h" 20 #include "chrome/browser/installable/installable_logging.h"
23 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/chrome_switches.h"
25 #include "components/content_settings/core/browser/host_content_settings_map.h" 23 #include "components/content_settings/core/browser/host_content_settings_map.h"
26 #include "components/content_settings/core/common/content_settings_pattern.h" 24 #include "components/content_settings/core/common/content_settings_pattern.h"
27 #include "components/rappor/public/rappor_utils.h" 25 #include "components/rappor/public/rappor_utils.h"
28 #include "components/rappor/rappor_service_impl.h" 26 #include "components/rappor/rappor_service_impl.h"
29 #include "components/variations/variations_associated_data.h" 27 #include "components/variations/variations_associated_data.h"
30 #include "content/public/browser/web_contents.h" 28 #include "content/public/browser/web_contents.h"
31 #include "net/base/escape.h"
32 #include "url/gurl.h" 29 #include "url/gurl.h"
33 30
34 namespace { 31 namespace {
35 32
36 // Max number of apps (including ServiceWorker based web apps) that a particular 33 // Max number of apps (including ServiceWorker based web apps) that a particular
37 // site may show a banner for. 34 // site may show a banner for.
38 const size_t kMaxAppsPerSite = 3; 35 const size_t kMaxAppsPerSite = 3;
39 36
40 // Oldest could show banner event we care about, in days.
41 const unsigned int kOldestCouldShowBannerEventInDays = 14;
42
43 // Default number of days that dismissing or ignoring the banner will prevent it 37 // Default number of days that dismissing or ignoring the banner will prevent it
44 // being seen again for. 38 // being seen again for.
45 const unsigned int kMinimumBannerBlockedToBannerShown = 90; 39 const unsigned int kMinimumBannerBlockedToBannerShown = 90;
46 const unsigned int kMinimumDaysBetweenBannerShows = 14; 40 const unsigned int kMinimumDaysBetweenBannerShows = 14;
47 41
48 const unsigned int kNumberOfMinutesInADay = 1440; 42 // Default site engagement required to trigger the banner.
49
50 // Default scores assigned to direct and indirect navigations respectively.
51 const unsigned int kDefaultDirectNavigationEngagement = 1;
52 const unsigned int kDefaultIndirectNavigationEngagement = 1;
53
54 // Default number of navigations required to trigger the banner.
55 const unsigned int kDefaultTotalEngagementToTrigger = 2; 43 const unsigned int kDefaultTotalEngagementToTrigger = 2;
56 44
57 // The number of days in the past that a site should be launched from homescreen 45 // The number of days in the past that a site should be launched from homescreen
58 // to be considered recent. 46 // to be considered recent.
59 // TODO(dominickn): work out how to unify this with 47 // TODO(dominickn): work out how to unify this with
60 // WebappDataStorage.wasLaunchedRecently. 48 // WebappDataStorage.wasLaunchedRecently.
61 const unsigned int kRecentLastLaunchInDays = 10; 49 const unsigned int kRecentLastLaunchInDays = 10;
62 50
63 // Dictionary keys to use for the events. 51 // Dictionary keys to use for the events. Must be kept in sync with
52 // AppBannerEvent.
64 const char* kBannerEventKeys[] = { 53 const char* kBannerEventKeys[] = {
65 "couldShowBannerEvents", 54 "couldShowBannerEvents",
66 "didShowBannerEvent", 55 "didShowBannerEvent",
67 "didBlockBannerEvent", 56 "didBlockBannerEvent",
68 "didAddToHomescreenEvent", 57 "didAddToHomescreenEvent",
69 }; 58 };
70 59
71 // Keys to use when storing BannerEvent structs.
72 const char kBannerTimeKey[] = "time";
73 const char kBannerEngagementKey[] = "engagement";
74
75 // Keys to use when querying the variations params. 60 // Keys to use when querying the variations params.
76 const char kBannerParamsKey[] = "AppBannerTriggering"; 61 const char kBannerParamsKey[] = "AppBannerTriggering";
77 const char kBannerParamsDirectKey[] = "direct";
78 const char kBannerParamsIndirectKey[] = "indirect";
79 const char kBannerParamsTotalKey[] = "total";
80 const char kBannerParamsMinutesKey[] = "minutes";
81 const char kBannerParamsEngagementTotalKey[] = "site_engagement_total"; 62 const char kBannerParamsEngagementTotalKey[] = "site_engagement_total";
82 const char kBannerParamsDaysAfterBannerDismissedKey[] = "days_after_dismiss"; 63 const char kBannerParamsDaysAfterBannerDismissedKey[] = "days_after_dismiss";
83 const char kBannerParamsDaysAfterBannerIgnoredKey[] = "days_after_ignore"; 64 const char kBannerParamsDaysAfterBannerIgnoredKey[] = "days_after_ignore";
84 const char kBannerSiteEngagementParamsKey[] = "use_site_engagement";
85 const char kBannerParamsLanguageKey[] = "language_option"; 65 const char kBannerParamsLanguageKey[] = "language_option";
86 66
87 // Engagement weight assigned to direct and indirect navigations.
88 // By default, a direct navigation is a page visit via ui::PAGE_TRANSITION_TYPED
89 // or ui::PAGE_TRANSITION_GENERATED.
90 double gDirectNavigationEngagement = kDefaultDirectNavigationEngagement;
91 double gIndirectNavigationEnagagement = kDefaultIndirectNavigationEngagement;
92
93 // Number of minutes between visits that will trigger a could show banner event.
94 // Defaults to the number of minutes in a day.
95 unsigned int gMinimumMinutesBetweenVisits = kNumberOfMinutesInADay;
96
97 // Total engagement score required before a banner will actually be triggered. 67 // Total engagement score required before a banner will actually be triggered.
98 double gTotalEngagementToTrigger = kDefaultTotalEngagementToTrigger; 68 double gTotalEngagementToTrigger = kDefaultTotalEngagementToTrigger;
99 69
100 unsigned int gDaysAfterDismissedToShow = kMinimumBannerBlockedToBannerShown; 70 unsigned int gDaysAfterDismissedToShow = kMinimumBannerBlockedToBannerShown;
101 unsigned int gDaysAfterIgnoredToShow = kMinimumDaysBetweenBannerShows; 71 unsigned int gDaysAfterIgnoredToShow = kMinimumDaysBetweenBannerShows;
102 72
103 std::unique_ptr<base::DictionaryValue> GetOriginDict( 73 std::unique_ptr<base::DictionaryValue> GetOriginDict(
104 HostContentSettingsMap* settings, 74 HostContentSettingsMap* settings,
105 const GURL& origin_url) { 75 const GURL& origin_url) {
106 if (!settings) 76 if (!settings)
(...skipping 17 matching lines...) Expand all
124 if (origin_dict->size() < kMaxAppsPerSite) { 94 if (origin_dict->size() < kMaxAppsPerSite) {
125 app_dict = new base::DictionaryValue(); 95 app_dict = new base::DictionaryValue();
126 origin_dict->SetWithoutPathExpansion(key_name, 96 origin_dict->SetWithoutPathExpansion(key_name,
127 base::WrapUnique(app_dict)); 97 base::WrapUnique(app_dict));
128 } 98 }
129 } 99 }
130 100
131 return app_dict; 101 return app_dict;
132 } 102 }
133 103
134 double GetEventEngagement(ui::PageTransition transition_type) {
135 if (ui::PageTransitionCoreTypeIs(transition_type,
136 ui::PAGE_TRANSITION_TYPED) ||
137 ui::PageTransitionCoreTypeIs(transition_type,
138 ui::PAGE_TRANSITION_GENERATED)) {
139 return gDirectNavigationEngagement;
140 } else {
141 return gIndirectNavigationEnagagement;
142 }
143 }
144
145 // Queries variations for the number of days which dismissing and ignoring the 104 // Queries variations for the number of days which dismissing and ignoring the
146 // banner should prevent a banner from showing. 105 // banner should prevent a banner from showing.
147 void UpdateDaysBetweenShowing() { 106 void UpdateDaysBetweenShowing() {
148 std::string dismiss_param = variations::GetVariationParamValue( 107 std::string dismiss_param = variations::GetVariationParamValue(
149 kBannerParamsKey, kBannerParamsDaysAfterBannerDismissedKey); 108 kBannerParamsKey, kBannerParamsDaysAfterBannerDismissedKey);
150 std::string ignore_param = variations::GetVariationParamValue( 109 std::string ignore_param = variations::GetVariationParamValue(
151 kBannerParamsKey, kBannerParamsDaysAfterBannerIgnoredKey); 110 kBannerParamsKey, kBannerParamsDaysAfterBannerIgnoredKey);
152 111
153 if (!dismiss_param.empty() && !ignore_param.empty()) { 112 if (!dismiss_param.empty() && !ignore_param.empty()) {
154 unsigned int dismiss_days = 0; 113 unsigned int dismiss_days = 0;
(...skipping 16 matching lines...) Expand all
171 if (!total_param.empty()) { 130 if (!total_param.empty()) {
172 double total_engagement = -1; 131 double total_engagement = -1;
173 132
174 if (base::StringToDouble(total_param, &total_engagement) && 133 if (base::StringToDouble(total_param, &total_engagement) &&
175 total_engagement >= 0) { 134 total_engagement >= 0) {
176 AppBannerSettingsHelper::SetTotalEngagementToTrigger(total_engagement); 135 AppBannerSettingsHelper::SetTotalEngagementToTrigger(total_engagement);
177 } 136 }
178 } 137 }
179 } 138 }
180 139
181 // Queries variations for updates to the default engagement values assigned
182 // to direct and indirect navigations.
183 void UpdateEngagementWeights() {
184 std::string direct_param = variations::GetVariationParamValue(
185 kBannerParamsKey, kBannerParamsDirectKey);
186 std::string indirect_param = variations::GetVariationParamValue(
187 kBannerParamsKey, kBannerParamsIndirectKey);
188 std::string total_param = variations::GetVariationParamValue(
189 kBannerParamsKey, kBannerParamsTotalKey);
190
191 if (!direct_param.empty() && !indirect_param.empty() &&
192 !total_param.empty()) {
193 double direct_engagement = -1;
194 double indirect_engagement = -1;
195 double total_engagement = -1;
196
197 // Ensure that we get valid doubles from the field trial, and that both
198 // values are greater than or equal to zero and less than or equal to the
199 // total engagement required to trigger the banner.
200 if (base::StringToDouble(direct_param, &direct_engagement) &&
201 base::StringToDouble(indirect_param, &indirect_engagement) &&
202 base::StringToDouble(total_param, &total_engagement) &&
203 direct_engagement >= 0 && indirect_engagement >= 0 &&
204 total_engagement > 0 && direct_engagement <= total_engagement &&
205 indirect_engagement <= total_engagement) {
206 AppBannerSettingsHelper::SetEngagementWeights(direct_engagement,
207 indirect_engagement);
208 AppBannerSettingsHelper::SetTotalEngagementToTrigger(total_engagement);
209 }
210 }
211 }
212
213 // Queries variation for updates to the default number of minutes between
214 // site visits counted for the purposes of displaying a banner.
215 void UpdateMinutesBetweenVisits() {
216 std::string param = variations::GetVariationParamValue(
217 kBannerParamsKey, kBannerParamsMinutesKey);
218 if (!param.empty()) {
219 int minimum_minutes = 0;
220 if (base::StringToInt(param, &minimum_minutes))
221 AppBannerSettingsHelper::SetMinimumMinutesBetweenVisits(minimum_minutes);
222 }
223 }
224
225 } // namespace 140 } // namespace
226 141
227 // Key to store instant apps events. 142 // Key to store instant apps events.
228 const char AppBannerSettingsHelper::kInstantAppsKey[] = "instantapps"; 143 const char AppBannerSettingsHelper::kInstantAppsKey[] = "instantapps";
229 144
230 void AppBannerSettingsHelper::ClearHistoryForURLs( 145 void AppBannerSettingsHelper::ClearHistoryForURLs(
231 Profile* profile, 146 Profile* profile,
232 const std::set<GURL>& origin_urls) { 147 const std::set<GURL>& origin_urls) {
233 HostContentSettingsMap* settings = 148 HostContentSettingsMap* settings =
234 HostContentSettingsMapFactory::GetForProfile(profile); 149 HostContentSettingsMapFactory::GetForProfile(profile);
235 for (const GURL& origin_url : origin_urls) { 150 for (const GURL& origin_url : origin_urls) {
236 settings->SetWebsiteSettingDefaultScope(origin_url, GURL(), 151 settings->SetWebsiteSettingDefaultScope(origin_url, GURL(),
237 CONTENT_SETTINGS_TYPE_APP_BANNER, 152 CONTENT_SETTINGS_TYPE_APP_BANNER,
238 std::string(), nullptr); 153 std::string(), nullptr);
239 settings->FlushLossyWebsiteSettings(); 154 settings->FlushLossyWebsiteSettings();
240 } 155 }
241 } 156 }
242 157
243 void AppBannerSettingsHelper::RecordBannerInstallEvent( 158 void AppBannerSettingsHelper::RecordBannerInstallEvent(
244 content::WebContents* web_contents, 159 content::WebContents* web_contents,
245 const std::string& package_name_or_start_url, 160 const std::string& package_name_or_start_url,
246 AppBannerRapporMetric rappor_metric) { 161 AppBannerRapporMetric rappor_metric) {
247 banners::TrackInstallEvent(banners::INSTALL_EVENT_WEB_APP_INSTALLED); 162 banners::TrackInstallEvent(banners::INSTALL_EVENT_WEB_APP_INSTALLED);
248 163
249 AppBannerSettingsHelper::RecordBannerEvent( 164 AppBannerSettingsHelper::RecordBannerEvent(
250 web_contents, web_contents->GetURL(), package_name_or_start_url, 165 web_contents, web_contents->GetLastCommittedURL(),
benwells 2016/12/09 04:55:05 Comment: It would probably have been wiser to put
dominickn 2016/12/09 05:27:28 Acknowledged - I was doing so much surgery here th
benwells 2016/12/09 05:44:55 Understood. I'd leave it as is, it was more a comm
166 package_name_or_start_url,
251 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN, 167 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
252 banners::AppBannerManager::GetCurrentTime()); 168 banners::AppBannerManager::GetCurrentTime());
253 169
254 rappor::SampleDomainAndRegistryFromGURL( 170 rappor::SampleDomainAndRegistryFromGURL(
255 g_browser_process->rappor_service(), 171 g_browser_process->rappor_service(),
256 (rappor_metric == WEB ? "AppBanner.WebApp.Installed" 172 (rappor_metric == WEB ? "AppBanner.WebApp.Installed"
257 : "AppBanner.NativeApp.Installed"), 173 : "AppBanner.NativeApp.Installed"),
258 web_contents->GetURL()); 174 web_contents->GetLastCommittedURL());
259 } 175 }
260 176
261 void AppBannerSettingsHelper::RecordBannerDismissEvent( 177 void AppBannerSettingsHelper::RecordBannerDismissEvent(
262 content::WebContents* web_contents, 178 content::WebContents* web_contents,
263 const std::string& package_name_or_start_url, 179 const std::string& package_name_or_start_url,
264 AppBannerRapporMetric rappor_metric) { 180 AppBannerRapporMetric rappor_metric) {
265 banners::TrackDismissEvent(banners::DISMISS_EVENT_CLOSE_BUTTON); 181 banners::TrackDismissEvent(banners::DISMISS_EVENT_CLOSE_BUTTON);
266 182
267 AppBannerSettingsHelper::RecordBannerEvent( 183 AppBannerSettingsHelper::RecordBannerEvent(
268 web_contents, web_contents->GetURL(), package_name_or_start_url, 184 web_contents, web_contents->GetLastCommittedURL(),
185 package_name_or_start_url,
269 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK, 186 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
270 banners::AppBannerManager::GetCurrentTime()); 187 banners::AppBannerManager::GetCurrentTime());
271 188
272 rappor::SampleDomainAndRegistryFromGURL( 189 rappor::SampleDomainAndRegistryFromGURL(
273 g_browser_process->rappor_service(), 190 g_browser_process->rappor_service(),
274 (rappor_metric == WEB ? "AppBanner.WebApp.Dismissed" 191 (rappor_metric == WEB ? "AppBanner.WebApp.Dismissed"
275 : "AppBanner.NativeApp.Dismissed"), 192 : "AppBanner.NativeApp.Dismissed"),
276 web_contents->GetURL()); 193 web_contents->GetLastCommittedURL());
277 } 194 }
278 195
279 void AppBannerSettingsHelper::RecordBannerEvent( 196 void AppBannerSettingsHelper::RecordBannerEvent(
280 content::WebContents* web_contents, 197 content::WebContents* web_contents,
281 const GURL& origin_url, 198 const GURL& origin_url,
282 const std::string& package_name_or_start_url, 199 const std::string& package_name_or_start_url,
283 AppBannerEvent event, 200 AppBannerEvent event,
284 base::Time time) { 201 base::Time time) {
285 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
286
287 Profile* profile = 202 Profile* profile =
288 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 203 Profile::FromBrowserContext(web_contents->GetBrowserContext());
289 if (profile->IsOffTheRecord() || package_name_or_start_url.empty()) 204 if (profile->IsOffTheRecord() || package_name_or_start_url.empty())
290 return; 205 return;
291 206
292 HostContentSettingsMap* settings = 207 HostContentSettingsMap* settings =
293 HostContentSettingsMapFactory::GetForProfile(profile); 208 HostContentSettingsMapFactory::GetForProfile(profile);
294 std::unique_ptr<base::DictionaryValue> origin_dict = 209 std::unique_ptr<base::DictionaryValue> origin_dict =
295 GetOriginDict(settings, origin_url); 210 GetOriginDict(settings, origin_url);
296 if (!origin_dict) 211 if (!origin_dict)
297 return; 212 return;
298 213
299 base::DictionaryValue* app_dict = 214 base::DictionaryValue* app_dict =
300 GetAppDict(origin_dict.get(), package_name_or_start_url); 215 GetAppDict(origin_dict.get(), package_name_or_start_url);
301 if (!app_dict) 216 if (!app_dict)
302 return; 217 return;
303 218
304 // Dates are stored in their raw form (i.e. not local dates) to be resilient 219 // Dates are stored in their raw form (i.e. not local dates) to be resilient
305 // to time zone changes. 220 // to time zone changes.
306 std::string event_key(kBannerEventKeys[event]); 221 std::string event_key(kBannerEventKeys[event]);
222
223 if (event == APP_BANNER_EVENT_COULD_SHOW) {
224 // Do not overwrite a could show event, as this is used for metrics.
225 double internal_date;
226 if (app_dict->GetDouble(event_key, &internal_date))
227 return;
228 }
307 app_dict->SetDouble(event_key, time.ToInternalValue()); 229 app_dict->SetDouble(event_key, time.ToInternalValue());
308 230
309 settings->SetWebsiteSettingDefaultScope( 231 settings->SetWebsiteSettingDefaultScope(
310 origin_url, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), 232 origin_url, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
311 std::move(origin_dict)); 233 std::move(origin_dict));
312 234
313 // App banner content settings are lossy, meaning they will not cause the 235 // App banner content settings are lossy, meaning they will not cause the
314 // prefs to become dirty. This is fine for most events, as if they are lost it 236 // prefs to become dirty. This is fine for most events, as if they are lost it
315 // just means the user will have to engage a little bit more. However the 237 // just means the user will have to engage a little bit more. However the
316 // DID_ADD_TO_HOMESCREEN event should always be recorded to prevent 238 // DID_ADD_TO_HOMESCREEN event should always be recorded to prevent
317 // spamminess. 239 // spamminess.
318 if (event == APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN) 240 if (event == APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN)
319 settings->FlushLossyWebsiteSettings(); 241 settings->FlushLossyWebsiteSettings();
320 } 242 }
321 243
322 void AppBannerSettingsHelper::RecordBannerCouldShowEvent(
323 content::WebContents* web_contents,
324 const GURL& origin_url,
325 const std::string& package_name_or_start_url,
326 base::Time time,
327 ui::PageTransition transition_type) {
328 Profile* profile =
329 Profile::FromBrowserContext(web_contents->GetBrowserContext());
330 if (profile->IsOffTheRecord() || package_name_or_start_url.empty())
331 return;
332
333 HostContentSettingsMap* settings =
334 HostContentSettingsMapFactory::GetForProfile(profile);
335 std::unique_ptr<base::DictionaryValue> origin_dict =
336 GetOriginDict(settings, origin_url);
337 if (!origin_dict)
338 return;
339
340 base::DictionaryValue* app_dict =
341 GetAppDict(origin_dict.get(), package_name_or_start_url);
342 if (!app_dict)
343 return;
344
345 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
346 double engagement = GetEventEngagement(transition_type);
347
348 base::ListValue* could_show_list = nullptr;
349 if (!app_dict->GetList(event_key, &could_show_list)) {
350 could_show_list = new base::ListValue();
351 app_dict->Set(event_key, base::WrapUnique(could_show_list));
352 }
353
354 // Trim any items that are older than we should care about. For comparisons
355 // the times are converted to local dates.
356 base::Time date = BucketTimeToResolution(time, gMinimumMinutesBetweenVisits);
357 for (auto it = could_show_list->begin(); it != could_show_list->end();) {
358 if ((*it)->IsType(base::Value::Type::DICTIONARY)) {
359 base::DictionaryValue* internal_value;
360 double internal_date;
361 (*it)->GetAsDictionary(&internal_value);
362
363 if (internal_value->GetDouble(kBannerTimeKey, &internal_date)) {
364 base::Time other_date =
365 BucketTimeToResolution(base::Time::FromInternalValue(internal_date),
366 gMinimumMinutesBetweenVisits);
367 if (other_date == date) {
368 double other_engagement = 0;
369 if (internal_value->GetDouble(kBannerEngagementKey,
370 &other_engagement) &&
371 other_engagement >= engagement) {
372 // This date has already been added, but with an equal or higher
373 // engagement. Don't add the date again. If the conditional fails,
374 // fall to the end of the loop where the existing entry is deleted.
375 return;
376 }
377 } else {
378 base::TimeDelta delta = date - other_date;
379 if (delta <
380 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
381 ++it;
382 continue;
383 }
384 }
385 }
386 }
387
388 // Either this date is older than we care about, or it isn't in the correct
389 // format, or it is the same as the current date but with a lower
390 // engagement, so remove it.
391 it = could_show_list->Erase(it, nullptr);
392 }
393
394 // Dates are stored in their raw form (i.e. not local dates) to be resilient
395 // to time zone changes.
396 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
397 value->SetDouble(kBannerTimeKey, time.ToInternalValue());
398 value->SetDouble(kBannerEngagementKey, engagement);
399 could_show_list->Append(std::move(value));
400
401 settings->SetWebsiteSettingDefaultScope(
402 origin_url, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
403 std::move(origin_dict));
404 }
405
406 InstallableStatusCode AppBannerSettingsHelper::ShouldShowBanner( 244 InstallableStatusCode AppBannerSettingsHelper::ShouldShowBanner(
407 content::WebContents* web_contents, 245 content::WebContents* web_contents,
408 const GURL& origin_url, 246 const GURL& origin_url,
409 const std::string& package_name_or_start_url, 247 const std::string& package_name_or_start_url,
410 base::Time time) { 248 base::Time time) {
411 // Ignore all checks if the flag to do so is set. 249 // Ignore all checks if the flag to do so is set.
412 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 250 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
413 switches::kBypassAppBannerEngagementChecks)) { 251 switches::kBypassAppBannerEngagementChecks)) {
414 return NO_ERROR_DETECTED; 252 return NO_ERROR_DETECTED;
415 } 253 }
(...skipping 20 matching lines...) Expand all
436 return PREVIOUSLY_BLOCKED; 274 return PREVIOUSLY_BLOCKED;
437 } 275 }
438 276
439 base::Time shown_time = 277 base::Time shown_time =
440 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url, 278 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
441 APP_BANNER_EVENT_DID_SHOW); 279 APP_BANNER_EVENT_DID_SHOW);
442 if (time - shown_time < base::TimeDelta::FromDays(gDaysAfterIgnoredToShow)) { 280 if (time - shown_time < base::TimeDelta::FromDays(gDaysAfterIgnoredToShow)) {
443 return PREVIOUSLY_IGNORED; 281 return PREVIOUSLY_IGNORED;
444 } 282 }
445 283
446 // If we have gotten this far and want to use site engagement, the banner flow
447 // was triggered by the site engagement service informing the banner manager
448 // that sufficient engagement has been accumulated. Hence there is no need to
449 // check the total amount of engagement.
450 // TODO(dominickn): just return true here and remove all of the following code
451 // in this method when app banners have fully migrated to using site
452 // engagement as a trigger condition. See crbug.com/616322.
453 // Do not do engagement checks for instant app banners.
454 if (ShouldUseSiteEngagementScore() ||
455 package_name_or_start_url == kInstantAppsKey) {
456 return NO_ERROR_DETECTED;
457 }
458
459 double total_engagement = 0;
460 std::vector<BannerEvent> could_show_events = GetCouldShowBannerEvents(
461 web_contents, origin_url, package_name_or_start_url);
462
463 for (const auto& event : could_show_events)
464 total_engagement += event.engagement;
465
466 if (!HasSufficientEngagement(total_engagement))
467 return INSUFFICIENT_ENGAGEMENT;
468
469 return NO_ERROR_DETECTED; 284 return NO_ERROR_DETECTED;
470 } 285 }
471 286
472 std::vector<AppBannerSettingsHelper::BannerEvent>
473 AppBannerSettingsHelper::GetCouldShowBannerEvents(
474 content::WebContents* web_contents,
475 const GURL& origin_url,
476 const std::string& package_name_or_start_url) {
477 std::vector<BannerEvent> result;
478
479 Profile* profile =
480 Profile::FromBrowserContext(web_contents->GetBrowserContext());
481 HostContentSettingsMap* settings =
482 HostContentSettingsMapFactory::GetForProfile(profile);
483 std::unique_ptr<base::DictionaryValue> origin_dict =
484 GetOriginDict(settings, origin_url);
485
486 if (!origin_dict)
487 return result;
488
489 base::DictionaryValue* app_dict =
490 GetAppDict(origin_dict.get(), package_name_or_start_url);
491 if (!app_dict)
492 return result;
493
494 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
495 base::ListValue* could_show_list = nullptr;
496 if (!app_dict->GetList(event_key, &could_show_list))
497 return result;
498
499 for (const auto& value : *could_show_list) {
500 if (value->IsType(base::Value::Type::DICTIONARY)) {
501 base::DictionaryValue* internal_value;
502 double internal_date = 0;
503 value->GetAsDictionary(&internal_value);
504 double engagement = 0;
505
506 if (internal_value->GetDouble(kBannerTimeKey, &internal_date) &&
507 internal_value->GetDouble(kBannerEngagementKey, &engagement)) {
508 base::Time date = base::Time::FromInternalValue(internal_date);
509 result.push_back({date, engagement});
510 }
511 }
512 }
513
514 return result;
515 }
516
517 base::Time AppBannerSettingsHelper::GetSingleBannerEvent( 287 base::Time AppBannerSettingsHelper::GetSingleBannerEvent(
518 content::WebContents* web_contents, 288 content::WebContents* web_contents,
519 const GURL& origin_url, 289 const GURL& origin_url,
520 const std::string& package_name_or_start_url, 290 const std::string& package_name_or_start_url,
521 AppBannerEvent event) { 291 AppBannerEvent event) {
522 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
523 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS); 292 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
524 293
525 Profile* profile = 294 Profile* profile =
526 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 295 Profile::FromBrowserContext(web_contents->GetBrowserContext());
527 HostContentSettingsMap* settings = 296 HostContentSettingsMap* settings =
528 HostContentSettingsMapFactory::GetForProfile(profile); 297 HostContentSettingsMapFactory::GetForProfile(profile);
529 std::unique_ptr<base::DictionaryValue> origin_dict = 298 std::unique_ptr<base::DictionaryValue> origin_dict =
530 GetOriginDict(settings, origin_url); 299 GetOriginDict(settings, origin_url);
531 300
532 if (!origin_dict) 301 if (!origin_dict)
(...skipping 16 matching lines...) Expand all
549 return (base::CommandLine::ForCurrentProcess()->HasSwitch( 318 return (base::CommandLine::ForCurrentProcess()->HasSwitch(
550 switches::kBypassAppBannerEngagementChecks)) || 319 switches::kBypassAppBannerEngagementChecks)) ||
551 (total_engagement >= gTotalEngagementToTrigger); 320 (total_engagement >= gTotalEngagementToTrigger);
552 } 321 }
553 322
554 void AppBannerSettingsHelper::RecordMinutesFromFirstVisitToShow( 323 void AppBannerSettingsHelper::RecordMinutesFromFirstVisitToShow(
555 content::WebContents* web_contents, 324 content::WebContents* web_contents,
556 const GURL& origin_url, 325 const GURL& origin_url,
557 const std::string& package_name_or_start_url, 326 const std::string& package_name_or_start_url,
558 base::Time time) { 327 base::Time time) {
559 std::vector<BannerEvent> could_show_events = GetCouldShowBannerEvents( 328 base::Time could_show_time =
560 web_contents, origin_url, package_name_or_start_url); 329 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
330 APP_BANNER_EVENT_COULD_SHOW);
561 331
562 int minutes = 0; 332 int minutes = 0;
563 if (could_show_events.size()) 333 if (!could_show_time.is_null())
564 minutes = (time - could_show_events[0].time).InMinutes(); 334 minutes = (time - could_show_time).InMinutes();
565 335
566 banners::TrackMinutesFromFirstVisitToBannerShown(minutes); 336 banners::TrackMinutesFromFirstVisitToBannerShown(minutes);
567 } 337 }
568 338
569 bool AppBannerSettingsHelper::WasLaunchedRecently(Profile* profile, 339 bool AppBannerSettingsHelper::WasLaunchedRecently(Profile* profile,
570 const GURL& origin_url, 340 const GURL& origin_url,
571 base::Time now) { 341 base::Time now) {
572 HostContentSettingsMap* settings = 342 HostContentSettingsMap* settings =
573 HostContentSettingsMapFactory::GetForProfile(profile); 343 HostContentSettingsMapFactory::GetForProfile(profile);
574 std::unique_ptr<base::DictionaryValue> origin_dict = 344 std::unique_ptr<base::DictionaryValue> origin_dict =
575 GetOriginDict(settings, origin_url); 345 GetOriginDict(settings, origin_url);
576 346
577 if (!origin_dict) 347 if (!origin_dict)
578 return false; 348 return false;
579 349
580 // Iterate over everything in the content setting, which should be a set of 350 // Iterate over everything in the content setting, which should be a set of
581 // dictionaries per app path. If we find one that has been added to 351 // dictionaries per app path. If we find one that has been added to
582 // homescreen recently, return true. 352 // homescreen recently, return true.
353 base::TimeDelta recent_last_launch_in_days =
354 base::TimeDelta::FromDays(kRecentLastLaunchInDays);
583 for (base::DictionaryValue::Iterator it(*origin_dict); !it.IsAtEnd(); 355 for (base::DictionaryValue::Iterator it(*origin_dict); !it.IsAtEnd();
584 it.Advance()) { 356 it.Advance()) {
585 if (it.value().IsType(base::Value::Type::DICTIONARY)) { 357 if (it.value().IsType(base::Value::Type::DICTIONARY)) {
586 const base::DictionaryValue* value; 358 const base::DictionaryValue* value;
587 it.value().GetAsDictionary(&value); 359 it.value().GetAsDictionary(&value);
588 360
589 std::string event_key( 361 std::string event_key(
590 kBannerEventKeys[APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN]); 362 kBannerEventKeys[APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN]);
591 double internal_time; 363 double internal_time;
592 if (it.key() == kInstantAppsKey || 364 if (it.key() == kInstantAppsKey ||
593 !value->GetDouble(event_key, &internal_time)) { 365 !value->GetDouble(event_key, &internal_time)) {
594 continue; 366 continue;
595 } 367 }
596 368
597 base::Time added_time = base::Time::FromInternalValue(internal_time); 369 if ((now - base::Time::FromInternalValue(internal_time)) <=
598 370 recent_last_launch_in_days) {
599 if ((now - added_time) <=
600 base::TimeDelta::FromDays(kRecentLastLaunchInDays)) {
601 return true; 371 return true;
602 } 372 }
603 } 373 }
604 } 374 }
605 375
606 return false; 376 return false;
607 } 377 }
608 378
609 void AppBannerSettingsHelper::SetDaysAfterDismissAndIgnoreToTrigger( 379 void AppBannerSettingsHelper::SetDaysAfterDismissAndIgnoreToTrigger(
610 unsigned int dismiss_days, 380 unsigned int dismiss_days,
611 unsigned int ignore_days) { 381 unsigned int ignore_days) {
612 gDaysAfterDismissedToShow = dismiss_days; 382 gDaysAfterDismissedToShow = dismiss_days;
613 gDaysAfterIgnoredToShow = ignore_days; 383 gDaysAfterIgnoredToShow = ignore_days;
614 } 384 }
615 385
616 void AppBannerSettingsHelper::SetEngagementWeights(double direct_engagement,
617 double indirect_engagement) {
618 gDirectNavigationEngagement = direct_engagement;
619 gIndirectNavigationEnagagement = indirect_engagement;
620 }
621
622 void AppBannerSettingsHelper::SetMinimumMinutesBetweenVisits(
623 unsigned int minutes) {
624 gMinimumMinutesBetweenVisits = minutes;
625 }
626
627 void AppBannerSettingsHelper::SetTotalEngagementToTrigger( 386 void AppBannerSettingsHelper::SetTotalEngagementToTrigger(
628 double total_engagement) { 387 double total_engagement) {
629 gTotalEngagementToTrigger = total_engagement; 388 gTotalEngagementToTrigger = total_engagement;
630 } 389 }
631 390
632 void AppBannerSettingsHelper::SetDefaultParameters() { 391 void AppBannerSettingsHelper::SetDefaultParameters() {
633 SetDaysAfterDismissAndIgnoreToTrigger(kMinimumBannerBlockedToBannerShown,
634 kMinimumDaysBetweenBannerShows);
635 SetEngagementWeights(kDefaultDirectNavigationEngagement,
636 kDefaultIndirectNavigationEngagement);
637 SetMinimumMinutesBetweenVisits(kNumberOfMinutesInADay);
638 SetTotalEngagementToTrigger(kDefaultTotalEngagementToTrigger); 392 SetTotalEngagementToTrigger(kDefaultTotalEngagementToTrigger);
639 } 393 }
640 394
641 // Given a time, returns that time scoped to the nearest minute resolution
642 // locally. For example, if the resolution is one hour, this function will
643 // return the time to the closest (previous) hour in the local time zone.
644 base::Time AppBannerSettingsHelper::BucketTimeToResolution(
645 base::Time time,
646 unsigned int minutes) {
647 // Only support resolutions smaller than or equal to one day. Enforce
648 // that resolutions divide evenly into one day. Otherwise, default to a
649 // day resolution (each time converted to midnight local time).
650 if (minutes == 0 || minutes >= kNumberOfMinutesInADay ||
651 kNumberOfMinutesInADay % minutes != 0) {
652 return time.LocalMidnight();
653 }
654
655 // Extract the number of minutes past midnight in local time. Divide that
656 // number by the resolution size, and return the time converted to local
657 // midnight with the resulting truncated number added.
658 base::Time::Exploded exploded;
659 time.LocalExplode(&exploded);
660 int total_minutes = exploded.hour * 60 + exploded.minute;
661
662 // Use truncating integer division here.
663 return time.LocalMidnight() +
664 base::TimeDelta::FromMinutes((total_minutes / minutes) * minutes);
665 }
666
667 void AppBannerSettingsHelper::UpdateFromFieldTrial() { 395 void AppBannerSettingsHelper::UpdateFromFieldTrial() {
668 // If we are using the site engagement score, only extract the total 396 // If we are using the site engagement score, only extract the total
669 // engagement to trigger from the params variations. 397 // engagement to trigger from the params variations.
670 UpdateDaysBetweenShowing(); 398 UpdateDaysBetweenShowing();
671 if (ShouldUseSiteEngagementScore()) { 399 UpdateSiteEngagementToTrigger();
672 UpdateSiteEngagementToTrigger();
673 } else {
674 UpdateEngagementWeights();
675 UpdateMinutesBetweenVisits();
676 }
677 } 400 }
678 401
679 AppBannerSettingsHelper::LanguageOption 402 AppBannerSettingsHelper::LanguageOption
680 AppBannerSettingsHelper::GetHomescreenLanguageOption() { 403 AppBannerSettingsHelper::GetHomescreenLanguageOption() {
681 std::string param = variations::GetVariationParamValue( 404 std::string param = variations::GetVariationParamValue(
682 kBannerParamsKey, kBannerParamsLanguageKey); 405 kBannerParamsKey, kBannerParamsLanguageKey);
683 unsigned int language_option = 0; 406 unsigned int language_option = 0;
684 407
685 if (param.empty() || !base::StringToUint(param, &language_option) || 408 if (param.empty() || !base::StringToUint(param, &language_option) ||
686 language_option < LANGUAGE_OPTION_MIN || 409 language_option < LANGUAGE_OPTION_MIN ||
687 language_option > LANGUAGE_OPTION_MAX) { 410 language_option > LANGUAGE_OPTION_MAX) {
688 return LANGUAGE_OPTION_DEFAULT; 411 return LANGUAGE_OPTION_DEFAULT;
689 } 412 }
690 413
691 return static_cast<LanguageOption>(language_option); 414 return static_cast<LanguageOption>(language_option);
692 } 415 }
693
694 bool AppBannerSettingsHelper::ShouldUseSiteEngagementScore() {
695 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
696 switches::kEnableSiteEngagementAppBanner)) {
697 return true;
698 }
699
700 // Assume any value which is not "0" or "false" indicates that we should use
701 // site engagement.
702 std::string param = variations::GetVariationParamValue(
703 kBannerParamsKey, kBannerSiteEngagementParamsKey);
704
705 return (!param.empty() && param != "0" && param != "false");
706 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698