[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / chrome / browser / banners / app_banner_settings_helper.cc
blob6ddde2a03ff8aed68da241e87513c3b39708bfc3
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_data_fetcher.h"
12 #include "chrome/browser/banners/app_banner_metrics.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "components/content_settings/core/browser/host_content_settings_map.h"
17 #include "components/content_settings/core/common/content_settings_pattern.h"
18 #include "components/rappor/rappor_utils.h"
19 #include "content/public/browser/web_contents.h"
20 #include "net/base/escape.h"
21 #include "url/gurl.h"
23 namespace {
25 // Max number of apps (including ServiceWorker based web apps) that a particular
26 // site may show a banner for.
27 const size_t kMaxAppsPerSite = 3;
29 // Oldest could show banner event we care about, in days.
30 const unsigned int kOldestCouldShowBannerEventInDays = 14;
32 // Number of times that the banner could have been shown before the banner will
33 // actually be triggered.
34 const unsigned int kCouldShowEventsToTrigger = 2;
36 // Number of days that showing the banner will prevent it being seen again for.
37 const unsigned int kMinimumDaysBetweenBannerShows = 60;
39 // Number of days that the banner being blocked will prevent it being seen again
40 // for.
41 const unsigned int kMinimumBannerBlockedToBannerShown = 90;
43 // Dictionary keys to use for the events.
44 const char* kBannerEventKeys[] = {
45 "couldShowBannerEvents",
46 "didShowBannerEvent",
47 "didBlockBannerEvent",
48 "didAddToHomescreenEvent",
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);
97 settings->FlushLossyWebsiteSettings();
101 void AppBannerSettingsHelper::RecordBannerInstallEvent(
102 content::WebContents* web_contents,
103 const std::string& package_name_or_start_url,
104 AppBannerRapporMetric rappor_metric) {
105 banners::TrackInstallEvent(banners::INSTALL_EVENT_WEB_APP_INSTALLED);
107 AppBannerSettingsHelper::RecordBannerEvent(
108 web_contents, web_contents->GetURL(),
109 package_name_or_start_url,
110 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
111 banners::AppBannerDataFetcher::GetCurrentTime());
113 rappor::SampleDomainAndRegistryFromGURL(
114 g_browser_process->rappor_service(),
115 (rappor_metric == WEB ? "AppBanner.WebApp.Installed"
116 : "AppBanner.NativeApp.Installed"),
117 web_contents->GetURL());
120 void AppBannerSettingsHelper::RecordBannerDismissEvent(
121 content::WebContents* web_contents,
122 const std::string& package_name_or_start_url,
123 AppBannerRapporMetric rappor_metric) {
124 banners::TrackDismissEvent(banners::DISMISS_EVENT_CLOSE_BUTTON);
126 AppBannerSettingsHelper::RecordBannerEvent(
127 web_contents, web_contents->GetURL(),
128 package_name_or_start_url,
129 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
130 banners::AppBannerDataFetcher::GetCurrentTime());
132 rappor::SampleDomainAndRegistryFromGURL(
133 g_browser_process->rappor_service(),
134 (rappor_metric == WEB ? "AppBanner.WebApp.Dismissed"
135 : "AppBanner.NativeApp.Dismissed"),
136 web_contents->GetURL());
139 void AppBannerSettingsHelper::RecordBannerEvent(
140 content::WebContents* web_contents,
141 const GURL& origin_url,
142 const std::string& package_name_or_start_url,
143 AppBannerEvent event,
144 base::Time time) {
145 Profile* profile =
146 Profile::FromBrowserContext(web_contents->GetBrowserContext());
147 if (profile->IsOffTheRecord() || package_name_or_start_url.empty())
148 return;
150 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url));
151 if (!pattern.IsValid())
152 return;
154 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
155 scoped_ptr<base::DictionaryValue> origin_dict =
156 GetOriginDict(settings, origin_url);
157 if (!origin_dict)
158 return;
160 base::DictionaryValue* app_dict =
161 GetAppDict(origin_dict.get(), package_name_or_start_url);
162 if (!app_dict)
163 return;
165 std::string event_key(kBannerEventKeys[event]);
167 if (event == APP_BANNER_EVENT_COULD_SHOW) {
168 base::ListValue* could_show_list = nullptr;
169 if (!app_dict->GetList(event_key, &could_show_list)) {
170 could_show_list = new base::ListValue();
171 app_dict->Set(event_key, make_scoped_ptr(could_show_list));
174 // Trim any items that are older than we should care about. For comparisons
175 // the times are converted to local dates.
176 base::Time date = time.LocalMidnight();
177 base::ValueVector::iterator it = could_show_list->begin();
178 while (it != could_show_list->end()) {
179 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) {
180 double internal_date;
181 (*it)->GetAsDouble(&internal_date);
182 base::Time other_date =
183 base::Time::FromInternalValue(internal_date).LocalMidnight();
184 // This date has already been added. Don't add the date again, and don't
185 // bother trimming values as it will have been done the first time the
186 // date was added (unless the local date has changed, which we can live
187 // with).
188 if (other_date == date)
189 return;
191 base::TimeDelta delta = date - other_date;
192 if (delta <
193 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) {
194 ++it;
195 continue;
199 // Either this date is older than we care about, or it isn't a date, so
200 // remove it;
201 it = could_show_list->Erase(it, nullptr);
204 // Dates are stored in their raw form (i.e. not local dates) to be resilient
205 // to time zone changes.
206 could_show_list->AppendDouble(time.ToInternalValue());
207 } else {
208 app_dict->SetDouble(event_key, time.ToInternalValue());
210 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
211 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
212 origin_dict.release());
214 // App banner content settings are lossy, meaning they will not cause the
215 // prefs to become dirty. This is fine for most events, as if they are lost it
216 // just means the user will have to engage a little bit more. However the
217 // DID_ADD_TO_HOMESCREEN event should always be recorded to prevent
218 // spamminess.
219 if (event == APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN)
220 settings->FlushLossyWebsiteSettings();
223 bool AppBannerSettingsHelper::ShouldShowBanner(
224 content::WebContents* web_contents,
225 const GURL& origin_url,
226 const std::string& package_name_or_start_url,
227 base::Time time) {
228 // Ignore all checks if the flag to do so is set.
229 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
230 switches::kBypassAppBannerEngagementChecks)) {
231 return true;
234 // Don't show if it has been added to the homescreen.
235 base::Time added_time =
236 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
237 APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN);
238 if (!added_time.is_null()) {
239 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
240 return false;
243 base::Time blocked_time =
244 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
245 APP_BANNER_EVENT_DID_BLOCK);
247 // Null times are in the distant past, so the delta between real times and
248 // null events will always be greater than the limits.
249 if (time - blocked_time <
250 base::TimeDelta::FromDays(kMinimumBannerBlockedToBannerShown)) {
251 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_BLOCKED_PREVIOUSLY);
252 return false;
255 base::Time shown_time =
256 GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
257 APP_BANNER_EVENT_DID_SHOW);
258 if (time - shown_time <
259 base::TimeDelta::FromDays(kMinimumDaysBetweenBannerShows)) {
260 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_IGNORED_PREVIOUSLY);
261 return false;
264 std::vector<base::Time> could_show_events = GetCouldShowBannerEvents(
265 web_contents, origin_url, package_name_or_start_url);
266 if (could_show_events.size() < kCouldShowEventsToTrigger) {
267 banners::TrackDisplayEvent(banners::DISPLAY_EVENT_NOT_VISITED_ENOUGH);
268 return false;
271 return true;
274 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents(
275 content::WebContents* web_contents,
276 const GURL& origin_url,
277 const std::string& package_name_or_start_url) {
278 std::vector<base::Time> result;
279 if (package_name_or_start_url.empty())
280 return result;
282 Profile* profile =
283 Profile::FromBrowserContext(web_contents->GetBrowserContext());
284 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
285 scoped_ptr<base::DictionaryValue> origin_dict =
286 GetOriginDict(settings, origin_url);
288 if (!origin_dict)
289 return result;
291 base::DictionaryValue* app_dict =
292 GetAppDict(origin_dict.get(), package_name_or_start_url);
293 if (!app_dict)
294 return result;
296 std::string event_key(kBannerEventKeys[APP_BANNER_EVENT_COULD_SHOW]);
297 base::ListValue* could_show_list = nullptr;
298 if (!app_dict->GetList(event_key, &could_show_list))
299 return result;
301 for (auto value : *could_show_list) {
302 if (value->IsType(base::Value::TYPE_DOUBLE)) {
303 double internal_date;
304 value->GetAsDouble(&internal_date);
305 base::Time date = base::Time::FromInternalValue(internal_date);
306 result.push_back(date);
310 return result;
313 base::Time AppBannerSettingsHelper::GetSingleBannerEvent(
314 content::WebContents* web_contents,
315 const GURL& origin_url,
316 const std::string& package_name_or_start_url,
317 AppBannerEvent event) {
318 DCHECK(event != APP_BANNER_EVENT_COULD_SHOW);
319 DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
321 if (package_name_or_start_url.empty())
322 return base::Time();
324 Profile* profile =
325 Profile::FromBrowserContext(web_contents->GetBrowserContext());
326 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap();
327 scoped_ptr<base::DictionaryValue> origin_dict =
328 GetOriginDict(settings, origin_url);
330 if (!origin_dict)
331 return base::Time();
333 base::DictionaryValue* app_dict =
334 GetAppDict(origin_dict.get(), package_name_or_start_url);
335 if (!app_dict)
336 return base::Time();
338 std::string event_key(kBannerEventKeys[event]);
339 double internal_time;
340 if (!app_dict->GetDouble(event_key, &internal_time))
341 return base::Time();
343 return base::Time::FromInternalValue(internal_time);