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

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

Issue 886643003: Use heuristic to work out when to prompt for app install banners. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove other patch Created 5 years, 10 months 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 <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 9
10 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/profiles/profile.h"
11 #include "components/content_settings/core/browser/host_content_settings_map.h" 11 #include "components/content_settings/core/browser/host_content_settings_map.h"
12 #include "components/content_settings/core/common/content_settings_pattern.h" 12 #include "components/content_settings/core/common/content_settings_pattern.h"
13 #include "content/public/browser/web_contents.h" 13 #include "content/public/browser/web_contents.h"
14 #include "net/base/escape.h" 14 #include "net/base/escape.h"
15 #include "url/gurl.h" 15 #include "url/gurl.h"
16 16
17 namespace { 17 namespace {
18 18
19 // Max number of apps (including ServiceWorker based web apps) that a particular 19 // Max number of apps (including ServiceWorker based web apps) that a particular
20 // site may show a banner for. 20 // site may show a banner for.
21 const size_t kMaxAppsPerSite = 3; 21 const size_t kMaxAppsPerSite = 3;
22 22
23 // Oldest could show banner event we care about, in days. 23 // Oldest could show banner event we care about, in days.
24 const unsigned int kOldestCouldShowBannerEventInDays = 14; 24 const unsigned int kOldestCouldShowBannerEventInDays = 14;
25 25
26 // Dictionary key to use for the 'could show banner' events. 26 // Number of times that the banner could have been shown before the banner will
27 const char kCouldShowBannerEventsKey[] = "couldShowBannerEvents"; 27 // actually be triggered.
28 const unsigned int kCouldShowEventsToTrigger = 2;
29
30 // Number of days that showing the banner will prevent it being seen again for.
31 const unsigned int kMinimumBannerShownToBannerShown = 60;
gone 2015/02/05 22:21:10 kMinimumDaysBetweenBannerShows?
benwells 2015/02/06 16:36:28 Done.
32
33 // Number of days that the banner being blocked will prevent it being seen again
34 // for.
35 const unsigned int kMinimumBannerBlockedToBannerShown = 180;
gone 2015/02/05 23:13:50 This is 6 months, right? I think the doc said 3?
benwells 2015/02/06 16:36:28 Done.
36
37 // Dictionary keys to use for the events.
38 const char* kBannerEventKeys[] = {
39 "couldShowBannerEvents",
40 "didShowBannerEvent",
41 "didBlockBannerEvent",
42 "didAddToHomescreenEvent",
43 };
28 44
29 // Dictionary key to use whether the banner has been blocked. 45 // Dictionary key to use whether the banner has been blocked.
30 const char kHasBlockedKey[] = "hasBlocked"; 46 const char kHasBlockedKey[] = "hasBlocked";
31 47
32 base::Time DateFromTime(base::Time time) {
33 base::Time::Exploded exploded;
34 time.LocalExplode(&exploded);
35 exploded.hour = 0;
36 exploded.minute = 0;
37 exploded.second = 0;
38 return base::Time::FromLocalExploded(exploded);
39 }
40
41 scoped_ptr<base::DictionaryValue> GetOriginDict( 48 scoped_ptr<base::DictionaryValue> GetOriginDict(
42 HostContentSettingsMap* settings, 49 HostContentSettingsMap* settings,
43 const GURL& origin_url) { 50 const GURL& origin_url) {
44 if (!settings) 51 if (!settings)
45 return scoped_ptr<base::DictionaryValue>(); 52 return scoped_ptr<base::DictionaryValue>();
46 53
47 scoped_ptr<base::Value> value = settings->GetWebsiteSetting( 54 scoped_ptr<base::Value> value = settings->GetWebsiteSetting(
48 origin_url, origin_url, CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), 55 origin_url, origin_url, CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
49 NULL); 56 NULL);
50 if (!value.get()) 57 if (!value.get())
(...skipping 14 matching lines...) Expand all
65 app_dict = new base::DictionaryValue(); 72 app_dict = new base::DictionaryValue();
66 origin_dict->SetWithoutPathExpansion(key_name, make_scoped_ptr(app_dict)); 73 origin_dict->SetWithoutPathExpansion(key_name, make_scoped_ptr(app_dict));
67 } 74 }
68 } 75 }
69 76
70 return app_dict; 77 return app_dict;
71 } 78 }
72 79
73 } // namespace 80 } // namespace
74 81
75 void AppBannerSettingsHelper::RecordCouldShowBannerEvent( 82 void AppBannerSettingsHelper::RecordBannerEvent(
76 content::WebContents* web_contents, 83 content::WebContents* web_contents,
77 const GURL& origin_url, 84 const GURL& origin_url,
78 const std::string& package_name_or_start_url, 85 const std::string& package_name_or_start_url,
86 AppBannerEvent event,
79 base::Time time) { 87 base::Time time) {
80 Profile* profile = 88 Profile* profile =
81 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 89 Profile::FromBrowserContext(web_contents->GetBrowserContext());
82 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || 90 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url ||
83 package_name_or_start_url.empty()) { 91 package_name_or_start_url.empty()) {
84 return; 92 return;
85 } 93 }
86 94
87 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); 95 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
88 if (!pattern.IsValid()) 96 if (!pattern.IsValid())
89 return; 97 return;
90 98
91 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); 99 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
92 scoped_ptr<base::DictionaryValue> origin_dict = 100 scoped_ptr<base::DictionaryValue> origin_dict =
93 GetOriginDict(settings, origin_url); 101 GetOriginDict(settings, origin_url);
94 if (!origin_dict) 102 if (!origin_dict)
95 return; 103 return;
96 104
97 base::DictionaryValue* app_dict = 105 base::DictionaryValue* app_dict =
98 GetAppDict(origin_dict.get(), package_name_or_start_url); 106 GetAppDict(origin_dict.get(), package_name_or_start_url);
99 if (!app_dict) 107 if (!app_dict)
100 return; 108 return;
101 109
102 base::ListValue* could_show_list = nullptr; 110 std::string event_key(kBannerEventKeys[event]);
103 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) {
104 could_show_list = new base::ListValue();
105 app_dict->Set(kCouldShowBannerEventsKey, make_scoped_ptr(could_show_list));
106 }
107 111
108 // Trim any items that are older than we should care about. For comparisons 112 if (event == APP_BANNER_EVENT_COULD_SHOW) {
109 // the times are converted to local dates. 113 base::ListValue* could_show_list = nullptr;
110 base::Time date = DateFromTime(time); 114 if (!app_dict->GetList(event_key, &could_show_list)) {
111 base::ValueVector::iterator it = could_show_list->begin(); 115 could_show_list = new base::ListValue();
112 while (it != could_show_list->end()) { 116 app_dict->Set(event_key, make_scoped_ptr(could_show_list));
113 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) {
114 double internal_date;
115 (*it)->GetAsDouble(&internal_date);
116 base::Time other_date =
117 DateFromTime(base::Time::FromInternalValue(internal_date));
118 // This date has already been added. Don't add the date again, and don't
119 // bother trimming values as it will have been done the first time the
120 // date was added (unless the local date has changed, which we can live
121 // with).
122 if (other_date == date)
123 return;
124
125 base::TimeDelta delta = date - other_date;
126 if (delta <
127 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
128 ++it;
129 continue;
130 }
131 } 117 }
132 118
133 // Either this date is older than we care about, or it isn't a date, so 119 // Trim any items that are older than we should care about. For comparisons
134 // remove it; 120 // the times are converted to local dates.
135 it = could_show_list->Erase(it, nullptr); 121 base::Time date = time.LocalMidnight();
122 base::ValueVector::iterator it = could_show_list->begin();
123 while (it != could_show_list->end()) {
124 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) {
125 double internal_date;
126 (*it)->GetAsDouble(&internal_date);
127 base::Time other_date =
128 base::Time::FromInternalValue(internal_date).LocalMidnight();
129 // This date has already been added. Don't add the date again, and don't
130 // bother trimming values as it will have been done the first time the
131 // date was added (unless the local date has changed, which we can live
132 // with).
133 if (other_date == date)
134 return;
135
136 base::TimeDelta delta = date - other_date;
137 if (delta <
138 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
139 ++it;
140 continue;
141 }
142 }
143
144 // Either this date is older than we care about, or it isn't a date, so
145 // remove it;
146 it = could_show_list->Erase(it, nullptr);
147 }
148
149 // Dates are stored in their raw form (i.e. not local dates) to be resilient
150 // to time zone changes.
151 could_show_list->AppendDouble(time.ToInternalValue());
152 } else {
153 app_dict->SetDouble(event_key, time.ToInternalValue());
136 } 154 }
137
138 // Dates are stored in their raw form (i.e. not local dates) to be resilient
139 // to time zone changes.
140 could_show_list->AppendDouble(time.ToInternalValue());
141 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), 155 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
142 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), 156 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
143 origin_dict.release()); 157 origin_dict.release());
144 } 158 }
145 159
160 bool AppBannerSettingsHelper::ShouldShowBanner(
161 content::WebContents* web_contents,
162 const GURL& origin_url,
163 const std::string& package_name_or_start_url,
164 base::Time time) {
165 // Don't show if it has been added to the homescreen.
166 base::Time added_time =
167 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
168 APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN);
169 if (!added_time.is_null())
170 return false;
171
172 base::Time blocked_time =
173 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
174 APP_BANNER_EVENT_DID_BLOCK);
175
176 // Null times are in the distant past, so the delta between real times and
177 // null events will always be greater than the limits.
178 if (time - blocked_time <
179 base::TimeDelta::FromDays(kMinimumBannerBlockedToBannerShown)) {
180 return false;
181 }
182
183 base::Time shown_time =
184 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
185 APP_BANNER_EVENT_DID_SHOW);
186 if (time - shown_time <
187 base::TimeDelta::FromDays(kMinimumBannerShownToBannerShown)) {
188 return false;
189 }
190
191 std::vector<base::Time> could_show_events = GetCouldShowBannerEvents(
192 web_contents, origin_url, package_name_or_start_url);
193 return could_show_events.size() >= kCouldShowEventsToTrigger;
194 }
195
146 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents( 196 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents(
147 content::WebContents* web_contents, 197 content::WebContents* web_contents,
148 const GURL& origin_url, 198 const GURL& origin_url,
149 const std::string& package_name_or_start_url) { 199 const std::string& package_name_or_start_url) {
150 std::vector<base::Time> result; 200 std::vector<base::Time> result;
151 if (package_name_or_start_url.empty()) 201 if (package_name_or_start_url.empty())
152 return result; 202 return result;
153 203
154 Profile* profile = 204 Profile* profile =
155 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 205 Profile::FromBrowserContext(web_contents->GetBrowserContext());
156 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); 206 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
157 scoped_ptr<base::DictionaryValue> origin_dict = 207 scoped_ptr<base::DictionaryValue> origin_dict =
158 GetOriginDict(settings, origin_url); 208 GetOriginDict(settings, origin_url);
159 209
160 if (!origin_dict) 210 if (!origin_dict)
161 return result; 211 return result;
162 212
163 base::DictionaryValue* app_dict = 213 base::DictionaryValue* app_dict =
164 GetAppDict(origin_dict.get(), package_name_or_start_url); 214 GetAppDict(origin_dict.get(), package_name_or_start_url);
165 if (!app_dict) 215 if (!app_dict)
166 return result; 216 return result;
167 217
218 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
168 base::ListValue* could_show_list = nullptr; 219 base::ListValue* could_show_list = nullptr;
169 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) 220 if (!app_dict->GetList(event_key, &could_show_list))
170 return result; 221 return result;
171 222
172 for (auto value : *could_show_list) { 223 for (auto value : *could_show_list) {
173 if (value->IsType(base::Value::TYPE_DOUBLE)) { 224 if (value->IsType(base::Value::TYPE_DOUBLE)) {
174 double internal_date; 225 double internal_date;
175 value->GetAsDouble(&internal_date); 226 value->GetAsDouble(&internal_date);
176 base::Time date = base::Time::FromInternalValue(internal_date); 227 base::Time date = base::Time::FromInternalValue(internal_date);
177 result.push_back(date); 228 result.push_back(date);
178 } 229 }
179 } 230 }
180 231
181 return result; 232 return result;
182 } 233 }
183 234
235 base::Time AppBannerSettingsHelper::GetSingleBannerEvent(
236 content::WebContents* web_contents,
237 const GURL& origin_url,
238 const std::string& package_name_or_start_url,
239 AppBannerEvent event) {
240 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
241 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
242
243 if (package_name_or_start_url.empty())
244 return base::Time();
245
246 Profile* profile =
247 Profile::FromBrowserContext(web_contents->GetBrowserContext());
248 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
249 scoped_ptr<base::DictionaryValue> origin_dict =
250 GetOriginDict(settings, origin_url);
251
252 if (!origin_dict)
253 return base::Time();
254
255 base::DictionaryValue* app_dict =
256 GetAppDict(origin_dict.get(), package_name_or_start_url);
257 if (!app_dict)
258 return base::Time();
259
260 std::string event_key(kBannerEventKeys[event]);
261 double internal_time;
262 if (!app_dict->GetDouble(event_key, &internal_time))
263 return base::Time();
264
265 return base::Time::FromInternalValue(internal_time);
266 }
267
184 bool AppBannerSettingsHelper::IsAllowed( 268 bool AppBannerSettingsHelper::IsAllowed(
185 content::WebContents* web_contents, 269 content::WebContents* web_contents,
186 const GURL& origin_url, 270 const GURL& origin_url,
187 const std::string& package_name_or_start_url) { 271 const std::string& package_name_or_start_url) {
188 Profile* profile = 272 Profile* profile =
189 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 273 Profile::FromBrowserContext(web_contents->GetBrowserContext());
190 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || 274 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url ||
191 package_name_or_start_url.empty()) { 275 package_name_or_start_url.empty()) {
192 return false; 276 return false;
193 } 277 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 GetAppDict(origin_dict.get(), package_name_or_start_url); 321 GetAppDict(origin_dict.get(), package_name_or_start_url);
238 if (!app_dict) 322 if (!app_dict)
239 return; 323 return;
240 324
241 // Update the setting and save it back. 325 // Update the setting and save it back.
242 app_dict->SetBoolean(kHasBlockedKey, true); 326 app_dict->SetBoolean(kHasBlockedKey, true);
243 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), 327 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
244 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), 328 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
245 origin_dict.release()); 329 origin_dict.release());
246 } 330 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698