OLD | NEW |
---|---|
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/android/banners/app_banner_settings_helper.h" | 5 #include "chrome/browser/android/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 "url/gurl.h" | 15 #include "url/gurl.h" |
15 | 16 |
16 namespace { | 17 namespace { |
17 std::string SanitizePackageName(std::string package_name) { | 18 |
18 // DictionaryValue doesn't allow '.' in the keys. Replace them with ' ' | 19 std::string SanitizeKeyName(std::string key_name) { |
19 // because you can't have a package name with a ' ' in it. | 20 // DictionaryValue doesn't allow '.' in the keys. So, first get rid of spaces |
Bernhard Bauer
2015/01/29 16:49:36
Yes, it does :) You can use SetWithoutPathExpansio
benwells
2015/01/30 06:27:02
Done.
| |
20 std::replace(package_name.begin(), package_name.end(), '.', ' '); | 21 // by escaping, then replace dots with spaces. |
21 return package_name; | 22 key_name = net::EscapeUrlEncodedData(key_name, false); |
23 std::replace(key_name.begin(), key_name.end(), '.', ' '); | |
24 return key_name; | |
22 } | 25 } |
23 | 26 |
24 // Max number of apps that a particular site may show a banner for. | 27 // Max number of apps (including web native apps) that a particular site may |
Bernhard Bauer
2015/01/29 16:49:35
What is a web native app? Does that mean Fizz?
benwells
2015/01/30 06:27:02
Yes it means Fizz. I'll explain better.
| |
28 // show a banner for. | |
25 const size_t kMaxAppsPerSite = 3; | 29 const size_t kMaxAppsPerSite = 3; |
26 } // namespace | |
27 | 30 |
28 bool AppBannerSettingsHelper::IsAllowed(content::WebContents* web_contents, | 31 // Oldest could show banner event we care about, in days. |
29 const GURL& origin_url, | 32 const unsigned int kOldestCouldShowBannerEventInDays = 14; |
30 const std::string& package_name) { | |
31 std::string sanitized_package_name = SanitizePackageName(package_name); | |
32 Profile* profile = | |
33 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
34 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
35 sanitized_package_name.empty()) { | |
36 return false; | |
37 } | |
38 | 33 |
39 // Check if this combination has been previously disabled. | 34 // Dictionary key to use for the 'could show banner' events. |
40 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | 35 const char kCouldShowBannerEventsKey[] = "couldShowBannerEvents"; |
36 | |
37 // Dictionary key to use whether the banner has been blocked. | |
38 const char kHasBlockedKey[] = "hasBlocked"; | |
39 | |
40 base::Time DateFromTime(base::Time time) { | |
41 base::Time::Exploded exploded; | |
42 time.LocalExplode(&exploded); | |
Bernhard Bauer
2015/01/29 16:49:36
If we are using local time here, there could of co
benwells
2015/01/30 06:27:02
Yep that's right, its a heuristic. I think the sam
| |
43 exploded.hour = 0; | |
44 exploded.minute = 0; | |
45 exploded.second = 0; | |
46 return base::Time::FromLocalExploded(exploded); | |
47 } | |
48 | |
49 scoped_ptr<base::DictionaryValue> GetOriginDict( | |
50 HostContentSettingsMap* settings, | |
51 const GURL& origin_url) { | |
41 if (!settings) | 52 if (!settings) |
42 return false; | 53 return scoped_ptr<base::DictionaryValue>(); |
54 | |
43 scoped_ptr<base::Value> value = | 55 scoped_ptr<base::Value> value = |
44 settings->GetWebsiteSetting(origin_url, | 56 settings->GetWebsiteSetting(origin_url, |
45 origin_url, | 57 origin_url, |
46 CONTENT_SETTINGS_TYPE_APP_BANNER, | 58 CONTENT_SETTINGS_TYPE_APP_BANNER, |
47 std::string(), | 59 std::string(), |
48 NULL); | 60 NULL); |
49 if (!value.get()) { | 61 if (!value.get()) { |
50 // We've never blocked a banner on this site. | 62 return scoped_ptr<base::DictionaryValue>(); |
51 return true; | 63 } |
52 } else if (value->IsType(base::Value::TYPE_DICTIONARY)) { | 64 |
53 // We expect to get a Dictionary back, where the keys are the package names. | 65 if (!value->IsType(base::Value::TYPE_DICTIONARY)) { |
54 base::DictionaryValue* banner_dict = | 66 return scoped_ptr<base::DictionaryValue>(); |
55 static_cast<base::DictionaryValue*>(value.get()); | 67 } |
56 bool is_allowed = false; | 68 |
57 if (banner_dict->GetBoolean(sanitized_package_name, &is_allowed)) { | 69 return scoped_ptr<base::DictionaryValue>( |
Bernhard Bauer
2015/01/29 16:49:35
You can use make_scoped_ptr() here, which is a bit
benwells
2015/01/30 06:27:02
nice, thanks. I think I've used it everywhere I ca
| |
58 return is_allowed; | 70 static_cast<base::DictionaryValue*>(value.release())); |
59 } else { | 71 } |
60 return banner_dict->size() < ::kMaxAppsPerSite; | 72 |
61 } | 73 base::DictionaryValue* GetAppDict(base::DictionaryValue* origin_dict, |
62 } else { | 74 const std::string& key_name) { |
63 // Somehow the value we got back is not a dictionary (e.g. the settings were | 75 base::DictionaryValue* app_dict = nullptr; |
64 // corrupted by malware). Try to clear it out. | 76 if (!origin_dict->GetDictionary(key_name, &app_dict)) { |
65 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); | 77 // Don't allow more than kMaxAppsPerSite dictionaries. |
66 if (pattern.IsValid()) { | 78 if (origin_dict->size() < kMaxAppsPerSite) { |
67 settings->SetWebsiteSetting(pattern, | 79 app_dict = new base::DictionaryValue(); |
68 ContentSettingsPattern::Wildcard(), | 80 origin_dict->Set(key_name, scoped_ptr<base::DictionaryValue>(app_dict)); |
Bernhard Bauer
2015/01/29 16:49:36
Same here, just use make_scoped_ptr.
benwells
2015/01/30 06:27:02
Done.
| |
69 CONTENT_SETTINGS_TYPE_APP_BANNER, | |
70 std::string(), | |
71 NULL); | |
72 return true; | |
73 } | 81 } |
74 } | 82 } |
75 | 83 |
76 return false; | 84 return app_dict; |
77 } | 85 } |
78 | 86 |
79 void AppBannerSettingsHelper::Block(content::WebContents* web_contents, | 87 } // namespace |
80 const GURL& origin_url, | |
81 const std::string& package_name) { | |
82 std::string sanitized_package_name = SanitizePackageName(package_name); | |
83 DCHECK(!sanitized_package_name.empty()); | |
84 | 88 |
89 void RecordCouldShowBannerEvent(content::WebContents* web_contents, | |
90 const GURL& origin_url, | |
91 const std::string& package_name_or_start_url, | |
92 base::Time time) { | |
93 std::string app_key = SanitizeKeyName(package_name_or_start_url); | |
85 Profile* profile = | 94 Profile* profile = |
86 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 95 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
87 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | 96 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
97 app_key.empty()) { | |
98 return; | |
99 } | |
100 | |
88 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); | 101 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); |
89 if (!settings || !pattern.IsValid()) | 102 if (!pattern.IsValid()) |
90 return; | 103 return; |
91 | 104 |
92 scoped_ptr<base::Value> value = | 105 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
93 settings->GetWebsiteSetting(origin_url, | 106 scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( |
94 origin_url, | 107 settings, origin_url); |
95 CONTENT_SETTINGS_TYPE_APP_BANNER, | 108 if (!origin_dict) |
96 std::string(), | 109 origin_dict.reset(new base::DictionaryValue()); |
97 NULL); | 110 |
98 base::DictionaryValue* banner_dict = NULL; | 111 base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); |
99 if (value.get() && value->IsType(base::Value::TYPE_DICTIONARY)) { | 112 if (!app_dict) |
100 banner_dict = static_cast<base::DictionaryValue*>(value.release()); | 113 return; |
101 } else { | 114 |
102 banner_dict = new base::DictionaryValue(); | 115 base::ListValue* could_show_list = nullptr; |
116 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) { | |
117 could_show_list = new base::ListValue(); | |
118 app_dict->Set(kCouldShowBannerEventsKey, | |
119 scoped_ptr<base::ListValue>(could_show_list)); | |
103 } | 120 } |
104 | 121 |
105 // Update the setting and save it back. | 122 // Trim any items that are older than we should care about. If the list |
106 banner_dict->SetBoolean(sanitized_package_name, false); | 123 // already contains the date of interest, just exit. |
124 base::Time date = DateFromTime(time); | |
125 for (base::ValueVector::iterator it = could_show_list->begin(); | |
126 it != could_show_list->end(); ++it) { | |
127 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) { | |
128 double internal_date; | |
129 DCHECK((*it)->GetAsDouble(&internal_date)); | |
Bernhard Bauer
2015/01/29 16:49:35
This will be compiled out in a Release build! The
benwells
2015/01/30 06:27:02
Ah, of course! Thanks for finding that. I don't th
| |
130 base::Time other_date = base::Time::FromInternalValue(internal_date); | |
131 if (other_date == date) | |
132 return; | |
133 | |
134 base::TimeDelta delta = date - other_date; | |
135 if (delta <= base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) | |
136 continue; | |
137 } | |
138 could_show_list->Erase(it, nullptr); | |
139 } | |
140 | |
141 could_show_list->AppendDouble(date.ToInternalValue()); | |
107 settings->SetWebsiteSetting(pattern, | 142 settings->SetWebsiteSetting(pattern, |
108 ContentSettingsPattern::Wildcard(), | 143 ContentSettingsPattern::Wildcard(), |
109 CONTENT_SETTINGS_TYPE_APP_BANNER, | 144 CONTENT_SETTINGS_TYPE_APP_BANNER, |
110 std::string(), | 145 std::string(), |
111 banner_dict); | 146 origin_dict.get()); |
112 } | 147 } |
148 | |
149 bool AppBannerSettingsHelper::IsAllowed( | |
150 content::WebContents* web_contents, | |
151 const GURL& origin_url, | |
152 const std::string& package_name_or_start_url) { | |
153 std::string app_key = SanitizeKeyName(package_name_or_start_url); | |
154 Profile* profile = | |
155 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
156 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
157 app_key.empty()) { | |
158 return false; | |
159 } | |
160 | |
161 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
162 scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( | |
163 settings, origin_url); | |
164 | |
165 if (!origin_dict) | |
166 return true; | |
167 | |
168 base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); | |
169 if (!app_dict) | |
170 return true; | |
171 | |
172 bool has_blocked; | |
173 if (!app_dict->GetBoolean(kHasBlockedKey, &has_blocked)) | |
174 return true; | |
175 | |
176 return !has_blocked; | |
177 } | |
178 | |
179 void AppBannerSettingsHelper::Block( | |
180 content::WebContents* web_contents, | |
181 const GURL& origin_url, | |
182 const std::string& package_name_or_start_url) { | |
183 std::string app_key = SanitizeKeyName(package_name_or_start_url); | |
184 Profile* profile = | |
185 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
186 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
187 app_key.empty()) { | |
188 return; | |
189 } | |
190 | |
191 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); | |
192 if (!pattern.IsValid()) | |
193 return; | |
194 | |
195 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
196 scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( | |
197 settings, origin_url); | |
198 | |
199 if (!origin_dict) | |
200 return; | |
201 | |
202 base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); | |
203 if (!app_dict) | |
204 return; | |
205 | |
206 // Update the setting and save it back. | |
207 app_dict->SetBoolean(kHasBlockedKey, false); | |
208 settings->SetWebsiteSetting(pattern, | |
209 ContentSettingsPattern::Wildcard(), | |
210 CONTENT_SETTINGS_TYPE_APP_BANNER, | |
211 std::string(), | |
212 origin_dict.get()); | |
213 } | |
OLD | NEW |