ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / banners / app_banner_settings_helper.cc
blob33c42ef08a21e70945aa20bc67542f2ddb456dab
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 // Dictionary key to use whether the banner has been blocked.
49 const char kHasBlockedKey[] = "hasBlocked";
51 scoped_ptr<base::DictionaryValue> GetOriginDict(
52 HostContentSettingsMap* settings,
53 const GURL& origin_url) {
54 if (!settings)
55 return scoped_ptr<base::DictionaryValue>();
57 scoped_ptr<base::Value> value = settings->GetWebsiteSetting(
58 origin_url, origin_url, CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
59 NULL);
60 if (!value.get())
61 return make_scoped_ptr(new base::DictionaryValue());
63 if (!value->IsType(base::Value::TYPE_DICTIONARY))
64 return make_scoped_ptr(new base::DictionaryValue());
66 return make_scoped_ptr(static_cast<base::DictionaryValue*>(value.release()));
69 base::DictionaryValue* GetAppDict(base::DictionaryValue* origin_dict,
70 const std::string& key_name) {
71 base::DictionaryValue* app_dict = nullptr;
72 if (!origin_dict->GetDictionaryWithoutPathExpansion(key_name, &app_dict)) {
73 // Don't allow more than kMaxAppsPerSite dictionaries.
74 if (origin_dict->size() < kMaxAppsPerSite) {
75 app_dict = new base::DictionaryValue();
76 origin_dict->SetWithoutPathExpansion(key_name, make_scoped_ptr(app_dict));
80 return app_dict;
83 } // namespace
85 void AppBannerSettingsHelper::ClearHistoryForURLs(
86 Profile* profile,
87 const std::set<GURL>& origin_urls) {
88 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
89 for (const GURL& origin_url : origin_urls) {
90 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
91 if (!pattern.IsValid())
92 continue;
94 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
95 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
96 nullptr);
100 void AppBannerSettingsHelper::RecordBannerEvent(
101 content::WebContents* web_contents,
102 const GURL& origin_url,
103 const std::string& package_name_or_start_url,
104 AppBannerEvent event,
105 base::Time time) {
106 Profile* profile =
107 Profile::FromBrowserContext(web_contents->GetBrowserContext());
108 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url ||
109 package_name_or_start_url.empty()) {
110 return;
113 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
114 if (!pattern.IsValid())
115 return;
117 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
118 scoped_ptr<base::DictionaryValue> origin_dict =
119 GetOriginDict(settings, origin_url);
120 if (!origin_dict)
121 return;
123 base::DictionaryValue* app_dict =
124 GetAppDict(origin_dict.get(), package_name_or_start_url);
125 if (!app_dict)
126 return;
128 std::string event_key(kBannerEventKeys[event]);
130 if (event == APP_BANNER_EVENT_COULD_SHOW) {
131 base::ListValue* could_show_list = nullptr;
132 if (!app_dict->GetList(event_key, &could_show_list)) {
133 could_show_list = new base::ListValue();
134 app_dict->Set(event_key, make_scoped_ptr(could_show_list));
137 // Trim any items that are older than we should care about. For comparisons
138 // the times are converted to local dates.
139 base::Time date = time.LocalMidnight();
140 base::ValueVector::iterator it = could_show_list->begin();
141 while (it != could_show_list->end()) {
142 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) {
143 double internal_date;
144 (*it)->GetAsDouble(&internal_date);
145 base::Time other_date =
146 base::Time::FromInternalValue(internal_date).LocalMidnight();
147 // This date has already been added. Don't add the date again, and don't
148 // bother trimming values as it will have been done the first time the
149 // date was added (unless the local date has changed, which we can live
150 // with).
151 if (other_date == date)
152 return;
154 base::TimeDelta delta = date - other_date;
155 if (delta <
156 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
157 ++it;
158 continue;
162 // Either this date is older than we care about, or it isn't a date, so
163 // remove it;
164 it = could_show_list->Erase(it, nullptr);
167 // Dates are stored in their raw form (i.e. not local dates) to be resilient
168 // to time zone changes.
169 could_show_list->AppendDouble(time.ToInternalValue());
170 } else {
171 app_dict->SetDouble(event_key, time.ToInternalValue());
173 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
174 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
175 origin_dict.release());
178 bool AppBannerSettingsHelper::ShouldShowBanner(
179 content::WebContents* web_contents,
180 const GURL& origin_url,
181 const std::string& package_name_or_start_url,
182 base::Time time) {
183 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
184 switches::kBypassAppBannerEngagementChecks)) {
185 return true;
188 // Don't show if it has been added to the homescreen.
189 base::Time added_time =
190 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
191 APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN);
192 if (!added_time.is_null()) {
193 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
194 return false;
197 base::Time blocked_time =
198 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
199 APP_BANNER_EVENT_DID_BLOCK);
201 // Null times are in the distant past, so the delta between real times and
202 // null events will always be greater than the limits.
203 if (time - blocked_time <
204 base::TimeDelta::FromDays(kMinimumBannerBlockedToBannerShown)) {
205 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_BLOCKED_PREVIOUSLY);
206 return false;
209 base::Time shown_time =
210 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
211 APP_BANNER_EVENT_DID_SHOW);
212 if (time - shown_time <
213 base::TimeDelta::FromDays(kMinimumDaysBetweenBannerShows)) {
214 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_IGNORED_PREVIOUSLY);
215 return false;
218 std::vector<base::Time> could_show_events = GetCouldShowBannerEvents(
219 web_contents, origin_url, package_name_or_start_url);
220 if (could_show_events.size() < kCouldShowEventsToTrigger) {
221 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_NOT_VISITED_ENOUGH);
222 return false;
225 return true;
228 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents(
229 content::WebContents* web_contents,
230 const GURL& origin_url,
231 const std::string& package_name_or_start_url) {
232 std::vector<base::Time> result;
233 if (package_name_or_start_url.empty())
234 return result;
236 Profile* profile =
237 Profile::FromBrowserContext(web_contents->GetBrowserContext());
238 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
239 scoped_ptr<base::DictionaryValue> origin_dict =
240 GetOriginDict(settings, origin_url);
242 if (!origin_dict)
243 return result;
245 base::DictionaryValue* app_dict =
246 GetAppDict(origin_dict.get(), package_name_or_start_url);
247 if (!app_dict)
248 return result;
250 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
251 base::ListValue* could_show_list = nullptr;
252 if (!app_dict->GetList(event_key, &could_show_list))
253 return result;
255 for (auto value : *could_show_list) {
256 if (value->IsType(base::Value::TYPE_DOUBLE)) {
257 double internal_date;
258 value->GetAsDouble(&internal_date);
259 base::Time date = base::Time::FromInternalValue(internal_date);
260 result.push_back(date);
264 return result;
267 base::Time AppBannerSettingsHelper::GetSingleBannerEvent(
268 content::WebContents* web_contents,
269 const GURL& origin_url,
270 const std::string& package_name_or_start_url,
271 AppBannerEvent event) {
272 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
273 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
275 if (package_name_or_start_url.empty())
276 return base::Time();
278 Profile* profile =
279 Profile::FromBrowserContext(web_contents->GetBrowserContext());
280 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
281 scoped_ptr<base::DictionaryValue> origin_dict =
282 GetOriginDict(settings, origin_url);
284 if (!origin_dict)
285 return base::Time();
287 base::DictionaryValue* app_dict =
288 GetAppDict(origin_dict.get(), package_name_or_start_url);
289 if (!app_dict)
290 return base::Time();
292 std::string event_key(kBannerEventKeys[event]);
293 double internal_time;
294 if (!app_dict->GetDouble(event_key, &internal_time))
295 return base::Time();
297 return base::Time::FromInternalValue(internal_time);
300 bool AppBannerSettingsHelper::IsAllowed(
301 content::WebContents* web_contents,
302 const GURL& origin_url,
303 const std::string& package_name_or_start_url) {
304 Profile* profile =
305 Profile::FromBrowserContext(web_contents->GetBrowserContext());
306 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url ||
307 package_name_or_start_url.empty()) {
308 return false;
311 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
312 scoped_ptr<base::DictionaryValue> origin_dict =
313 GetOriginDict(settings, origin_url);
315 if (!origin_dict)
316 return true;
318 base::DictionaryValue* app_dict =
319 GetAppDict(origin_dict.get(), package_name_or_start_url);
320 if (!app_dict)
321 return true;
323 bool has_blocked;
324 if (!app_dict->GetBoolean(kHasBlockedKey, &has_blocked))
325 return true;
327 return !has_blocked;
330 void AppBannerSettingsHelper::Block(
331 content::WebContents* web_contents,
332 const GURL& origin_url,
333 const std::string& package_name_or_start_url) {
334 Profile* profile =
335 Profile::FromBrowserContext(web_contents->GetBrowserContext());
336 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url ||
337 package_name_or_start_url.empty()) {
338 return;
341 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
342 if (!pattern.IsValid())
343 return;
345 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
346 scoped_ptr<base::DictionaryValue> origin_dict =
347 GetOriginDict(settings, origin_url);
349 if (!origin_dict)
350 return;
352 base::DictionaryValue* app_dict =
353 GetAppDict(origin_dict.get(), package_name_or_start_url);
354 if (!app_dict)
355 return;
357 // Update the setting and save it back.
358 app_dict->SetBoolean(kHasBlockedKey, true);
359 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
360 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
361 origin_dict.release());