Chromium Code Reviews| 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 |