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

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

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

Powered by Google App Engine
This is Rietveld 408576698