Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / banners / app_banner_settings_helper.cc
blob02d442d429949f83e6dd7d68237f13e2c5e11f4a
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
3 // found in the LICENSE file.
5 #include "chrome/browser/banners/app_banner_settings_helper.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/command_line.h"
11 #include "chrome/browser/banners/app_banner_metrics.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "components/content_settings/core/browser/host_content_settings_map.h"
15 #include "components/content_settings/core/common/content_settings_pattern.h"
16 #include "content/public/browser/web_contents.h"
17 #include "net/base/escape.h"
18 #include "url/gurl.h"
20 namespace {
22 // Max number of apps (including ServiceWorker based web apps) that a particular
23 // site may show a banner for.
24 const size_t kMaxAppsPerSite = 3;
26 // Oldest could show banner event we care about, in days.
27 const unsigned int kOldestCouldShowBannerEventInDays = 14;
29 // Number of times that the banner could have been shown before the banner will
30 // actually be triggered.
31 const unsigned int kCouldShowEventsToTrigger = 2;
33 // Number of days that showing the banner will prevent it being seen again for.
34 const unsigned int kMinimumDaysBetweenBannerShows = 60;
36 // Number of days that the banner being blocked will prevent it being seen again
37 // for.
38 const unsigned int kMinimumBannerBlockedToBannerShown = 90;
40 // Dictionary keys to use for the events.
41 const char* kBannerEventKeys[] = {
42 "couldShowBannerEvents",
43 "didShowBannerEvent",
44 "didBlockBannerEvent",
45 "didAddToHomescreenEvent",
48 scoped_ptr<base::DictionaryValue> GetOriginDict(
49 HostContentSettingsMap* settings,
50 const GURL& origin_url) {
51 if (!settings)
52 return scoped_ptr<base::DictionaryValue>();
54 scoped_ptr<base::Value> value = settings->GetWebsiteSetting(
55 origin_url, origin_url, CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
56 NULL);
57 if (!value.get())
58 return make_scoped_ptr(new base::DictionaryValue());
60 if (!value->IsType(base::Value::TYPE_DICTIONARY))
61 return make_scoped_ptr(new base::DictionaryValue());
63 return make_scoped_ptr(static_cast<base::DictionaryValue*>(value.release()));
66 base::DictionaryValue* GetAppDict(base::DictionaryValue* origin_dict,
67 const std::string& key_name) {
68 base::DictionaryValue* app_dict = nullptr;
69 if (!origin_dict->GetDictionaryWithoutPathExpansion(key_name, &app_dict)) {
70 // Don't allow more than kMaxAppsPerSite dictionaries.
71 if (origin_dict->size() < kMaxAppsPerSite) {
72 app_dict = new base::DictionaryValue();
73 origin_dict->SetWithoutPathExpansion(key_name, make_scoped_ptr(app_dict));
77 return app_dict;
80 } // namespace
82 void AppBannerSettingsHelper::ClearHistoryForURLs(
83 Profile* profile,
84 const std::set<GURL>& origin_urls) {
85 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
86 for (const GURL& origin_url : origin_urls) {
87 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
88 if (!pattern.IsValid())
89 continue;
91 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
92 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
93 nullptr);
97 void AppBannerSettingsHelper::RecordBannerEvent(
98 content::WebContents* web_contents,
99 const GURL& origin_url,
100 const std::string& package_name_or_start_url,
101 AppBannerEvent event,
102 base::Time time) {
103 Profile* profile =
104 Profile::FromBrowserContext(web_contents->GetBrowserContext());
105 if (profile->IsOffTheRecord() || package_name_or_start_url.empty())
106 return;
108 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
109 if (!pattern.IsValid())
110 return;
112 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
113 scoped_ptr<base::DictionaryValue> origin_dict =
114 GetOriginDict(settings, origin_url);
115 if (!origin_dict)
116 return;
118 base::DictionaryValue* app_dict =
119 GetAppDict(origin_dict.get(), package_name_or_start_url);
120 if (!app_dict)
121 return;
123 std::string event_key(kBannerEventKeys[event]);
125 if (event == APP_BANNER_EVENT_COULD_SHOW) {
126 base::ListValue* could_show_list = nullptr;
127 if (!app_dict->GetList(event_key, &could_show_list)) {
128 could_show_list = new base::ListValue();
129 app_dict->Set(event_key, make_scoped_ptr(could_show_list));
132 // Trim any items that are older than we should care about. For comparisons
133 // the times are converted to local dates.
134 base::Time date = time.LocalMidnight();
135 base::ValueVector::iterator it = could_show_list->begin();
136 while (it != could_show_list->end()) {
137 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) {
138 double internal_date;
139 (*it)->GetAsDouble(&internal_date);
140 base::Time other_date =
141 base::Time::FromInternalValue(internal_date).LocalMidnight();
142 // This date has already been added. Don't add the date again, and don't
143 // bother trimming values as it will have been done the first time the
144 // date was added (unless the local date has changed, which we can live
145 // with).
146 if (other_date == date)
147 return;
149 base::TimeDelta delta = date - other_date;
150 if (delta <
151 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
152 ++it;
153 continue;
157 // Either this date is older than we care about, or it isn't a date, so
158 // remove it;
159 it = could_show_list->Erase(it, nullptr);
162 // Dates are stored in their raw form (i.e. not local dates) to be resilient
163 // to time zone changes.
164 could_show_list->AppendDouble(time.ToInternalValue());
165 } else {
166 app_dict->SetDouble(event_key, time.ToInternalValue());
168 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
169 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
170 origin_dict.release());
173 bool AppBannerSettingsHelper::ShouldShowBanner(
174 content::WebContents* web_contents,
175 const GURL& origin_url,
176 const std::string& package_name_or_start_url,
177 base::Time time) {
178 // Don't show if it has been added to the homescreen.
179 base::Time added_time =
180 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
181 APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN);
182 if (!added_time.is_null()) {
183 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
184 return false;
187 // Otherwise, ignore all checks if the flag to do so is set.
188 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
189 switches::kBypassAppBannerEngagementChecks)) {
190 return true;
193 base::Time blocked_time =
194 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
195 APP_BANNER_EVENT_DID_BLOCK);
197 // Null times are in the distant past, so the delta between real times and
198 // null events will always be greater than the limits.
199 if (time - blocked_time <
200 base::TimeDelta::FromDays(kMinimumBannerBlockedToBannerShown)) {
201 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_BLOCKED_PREVIOUSLY);
202 return false;
205 base::Time shown_time =
206 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
207 APP_BANNER_EVENT_DID_SHOW);
208 if (time - shown_time <
209 base::TimeDelta::FromDays(kMinimumDaysBetweenBannerShows)) {
210 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_IGNORED_PREVIOUSLY);
211 return false;
214 std::vector<base::Time> could_show_events = GetCouldShowBannerEvents(
215 web_contents, origin_url, package_name_or_start_url);
216 if (could_show_events.size() < kCouldShowEventsToTrigger) {
217 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_NOT_VISITED_ENOUGH);
218 return false;
221 return true;
224 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents(
225 content::WebContents* web_contents,
226 const GURL& origin_url,
227 const std::string& package_name_or_start_url) {
228 std::vector<base::Time> result;
229 if (package_name_or_start_url.empty())
230 return result;
232 Profile* profile =
233 Profile::FromBrowserContext(web_contents->GetBrowserContext());
234 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
235 scoped_ptr<base::DictionaryValue> origin_dict =
236 GetOriginDict(settings, origin_url);
238 if (!origin_dict)
239 return result;
241 base::DictionaryValue* app_dict =
242 GetAppDict(origin_dict.get(), package_name_or_start_url);
243 if (!app_dict)
244 return result;
246 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
247 base::ListValue* could_show_list = nullptr;
248 if (!app_dict->GetList(event_key, &could_show_list))
249 return result;
251 for (auto value : *could_show_list) {
252 if (value->IsType(base::Value::TYPE_DOUBLE)) {
253 double internal_date;
254 value->GetAsDouble(&internal_date);
255 base::Time date = base::Time::FromInternalValue(internal_date);
256 result.push_back(date);
260 return result;
263 base::Time AppBannerSettingsHelper::GetSingleBannerEvent(
264 content::WebContents* web_contents,
265 const GURL& origin_url,
266 const std::string& package_name_or_start_url,
267 AppBannerEvent event) {
268 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
269 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
271 if (package_name_or_start_url.empty())
272 return base::Time();
274 Profile* profile =
275 Profile::FromBrowserContext(web_contents->GetBrowserContext());
276 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
277 scoped_ptr<base::DictionaryValue> origin_dict =
278 GetOriginDict(settings, origin_url);
280 if (!origin_dict)
281 return base::Time();
283 base::DictionaryValue* app_dict =
284 GetAppDict(origin_dict.get(), package_name_or_start_url);
285 if (!app_dict)
286 return base::Time();
288 std::string event_key(kBannerEventKeys[event]);
289 double internal_time;
290 if (!app_dict->GetDouble(event_key, &internal_time))
291 return base::Time();
293 return base::Time::FromInternalValue(internal_time);