Chromium Code Reviews| Index: chrome/browser/android/banners/app_banner_settings_helper.cc |
| diff --git a/chrome/browser/android/banners/app_banner_settings_helper.cc b/chrome/browser/android/banners/app_banner_settings_helper.cc |
| index ed3298ccbb948de4c283d71f759ae221b8c7b62f..aa418ba193dcba9b5a3cc1f3fbd8afd3d998b279 100644 |
| --- a/chrome/browser/android/banners/app_banner_settings_helper.cc |
| +++ b/chrome/browser/android/banners/app_banner_settings_helper.cc |
| @@ -11,35 +11,47 @@ |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "content/public/browser/web_contents.h" |
| +#include "net/base/escape.h" |
| #include "url/gurl.h" |
| namespace { |
| -std::string SanitizePackageName(std::string package_name) { |
| - // DictionaryValue doesn't allow '.' in the keys. Replace them with ' ' |
| - // because you can't have a package name with a ' ' in it. |
| - std::replace(package_name.begin(), package_name.end(), '.', ' '); |
| - return package_name; |
| + |
| +std::string SanitizeKeyName(std::string key_name) { |
| + // 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.
|
| + // by escaping, then replace dots with spaces. |
| + key_name = net::EscapeUrlEncodedData(key_name, false); |
| + std::replace(key_name.begin(), key_name.end(), '.', ' '); |
| + return key_name; |
| } |
| -// Max number of apps that a particular site may show a banner for. |
| +// 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.
|
| +// show a banner for. |
| const size_t kMaxAppsPerSite = 3; |
| -} // namespace |
| -bool AppBannerSettingsHelper::IsAllowed(content::WebContents* web_contents, |
| - const GURL& origin_url, |
| - const std::string& package_name) { |
| - std::string sanitized_package_name = SanitizePackageName(package_name); |
| - Profile* profile = |
| - Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| - if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| - sanitized_package_name.empty()) { |
| - return false; |
| - } |
| +// Oldest could show banner event we care about, in days. |
| +const unsigned int kOldestCouldShowBannerEventInDays = 14; |
| - // Check if this combination has been previously disabled. |
| - HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| +// Dictionary key to use for the 'could show banner' events. |
| +const char kCouldShowBannerEventsKey[] = "couldShowBannerEvents"; |
| + |
| +// Dictionary key to use whether the banner has been blocked. |
| +const char kHasBlockedKey[] = "hasBlocked"; |
| + |
| +base::Time DateFromTime(base::Time time) { |
| + base::Time::Exploded exploded; |
| + 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
|
| + exploded.hour = 0; |
| + exploded.minute = 0; |
| + exploded.second = 0; |
| + return base::Time::FromLocalExploded(exploded); |
| +} |
| + |
| +scoped_ptr<base::DictionaryValue> GetOriginDict( |
| + HostContentSettingsMap* settings, |
| + const GURL& origin_url) { |
| if (!settings) |
| - return false; |
| + return scoped_ptr<base::DictionaryValue>(); |
| + |
| scoped_ptr<base::Value> value = |
| settings->GetWebsiteSetting(origin_url, |
| origin_url, |
| @@ -47,66 +59,155 @@ bool AppBannerSettingsHelper::IsAllowed(content::WebContents* web_contents, |
| std::string(), |
| NULL); |
| if (!value.get()) { |
| - // We've never blocked a banner on this site. |
| - return true; |
| - } else if (value->IsType(base::Value::TYPE_DICTIONARY)) { |
| - // We expect to get a Dictionary back, where the keys are the package names. |
| - base::DictionaryValue* banner_dict = |
| - static_cast<base::DictionaryValue*>(value.get()); |
| - bool is_allowed = false; |
| - if (banner_dict->GetBoolean(sanitized_package_name, &is_allowed)) { |
| - return is_allowed; |
| - } else { |
| - return banner_dict->size() < ::kMaxAppsPerSite; |
| - } |
| - } else { |
| - // Somehow the value we got back is not a dictionary (e.g. the settings were |
| - // corrupted by malware). Try to clear it out. |
| - ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); |
| - if (pattern.IsValid()) { |
| - settings->SetWebsiteSetting(pattern, |
| - ContentSettingsPattern::Wildcard(), |
| - CONTENT_SETTINGS_TYPE_APP_BANNER, |
| - std::string(), |
| - NULL); |
| - return true; |
| + return scoped_ptr<base::DictionaryValue>(); |
| + } |
| + |
| + if (!value->IsType(base::Value::TYPE_DICTIONARY)) { |
| + return scoped_ptr<base::DictionaryValue>(); |
| + } |
| + |
| + 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
|
| + static_cast<base::DictionaryValue*>(value.release())); |
| +} |
| + |
| +base::DictionaryValue* GetAppDict(base::DictionaryValue* origin_dict, |
| + const std::string& key_name) { |
| + base::DictionaryValue* app_dict = nullptr; |
| + if (!origin_dict->GetDictionary(key_name, &app_dict)) { |
| + // Don't allow more than kMaxAppsPerSite dictionaries. |
| + if (origin_dict->size() < kMaxAppsPerSite) { |
| + app_dict = new base::DictionaryValue(); |
| + 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.
|
| } |
| } |
| - return false; |
| + return app_dict; |
| } |
| -void AppBannerSettingsHelper::Block(content::WebContents* web_contents, |
| - const GURL& origin_url, |
| - const std::string& package_name) { |
| - std::string sanitized_package_name = SanitizePackageName(package_name); |
| - DCHECK(!sanitized_package_name.empty()); |
| +} // namespace |
| +void RecordCouldShowBannerEvent(content::WebContents* web_contents, |
| + const GURL& origin_url, |
| + const std::string& package_name_or_start_url, |
| + base::Time time) { |
| + std::string app_key = SanitizeKeyName(package_name_or_start_url); |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| - HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| + if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| + app_key.empty()) { |
| + return; |
| + } |
| + |
| ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); |
| - if (!settings || !pattern.IsValid()) |
| + if (!pattern.IsValid()) |
| return; |
| - scoped_ptr<base::Value> value = |
| - settings->GetWebsiteSetting(origin_url, |
| - origin_url, |
| - CONTENT_SETTINGS_TYPE_APP_BANNER, |
| - std::string(), |
| - NULL); |
| - base::DictionaryValue* banner_dict = NULL; |
| - if (value.get() && value->IsType(base::Value::TYPE_DICTIONARY)) { |
| - banner_dict = static_cast<base::DictionaryValue*>(value.release()); |
| - } else { |
| - banner_dict = new base::DictionaryValue(); |
| + HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| + scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( |
| + settings, origin_url); |
| + if (!origin_dict) |
| + origin_dict.reset(new base::DictionaryValue()); |
| + |
| + base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); |
| + if (!app_dict) |
| + return; |
| + |
| + base::ListValue* could_show_list = nullptr; |
| + if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) { |
| + could_show_list = new base::ListValue(); |
| + app_dict->Set(kCouldShowBannerEventsKey, |
| + scoped_ptr<base::ListValue>(could_show_list)); |
| } |
| + // Trim any items that are older than we should care about. If the list |
| + // already contains the date of interest, just exit. |
| + base::Time date = DateFromTime(time); |
| + for (base::ValueVector::iterator it = could_show_list->begin(); |
| + it != could_show_list->end(); ++it) { |
| + if ((*it)->IsType(base::Value::TYPE_DOUBLE)) { |
| + double internal_date; |
| + 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
|
| + base::Time other_date = base::Time::FromInternalValue(internal_date); |
| + if (other_date == date) |
| + return; |
| + |
| + base::TimeDelta delta = date - other_date; |
| + if (delta <= base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) |
| + continue; |
| + } |
| + could_show_list->Erase(it, nullptr); |
| + } |
| + |
| + could_show_list->AppendDouble(date.ToInternalValue()); |
| + settings->SetWebsiteSetting(pattern, |
| + ContentSettingsPattern::Wildcard(), |
| + CONTENT_SETTINGS_TYPE_APP_BANNER, |
| + std::string(), |
| + origin_dict.get()); |
| +} |
| + |
| +bool AppBannerSettingsHelper::IsAllowed( |
| + content::WebContents* web_contents, |
| + const GURL& origin_url, |
| + const std::string& package_name_or_start_url) { |
| + std::string app_key = SanitizeKeyName(package_name_or_start_url); |
| + Profile* profile = |
| + Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| + if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| + app_key.empty()) { |
| + return false; |
| + } |
| + |
| + HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| + scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( |
| + settings, origin_url); |
| + |
| + if (!origin_dict) |
| + return true; |
| + |
| + base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); |
| + if (!app_dict) |
| + return true; |
| + |
| + bool has_blocked; |
| + if (!app_dict->GetBoolean(kHasBlockedKey, &has_blocked)) |
| + return true; |
| + |
| + return !has_blocked; |
| +} |
| + |
| +void AppBannerSettingsHelper::Block( |
| + content::WebContents* web_contents, |
| + const GURL& origin_url, |
| + const std::string& package_name_or_start_url) { |
| + std::string app_key = SanitizeKeyName(package_name_or_start_url); |
| + Profile* profile = |
| + Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| + if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| + app_key.empty()) { |
| + return; |
| + } |
| + |
| + ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); |
| + if (!pattern.IsValid()) |
| + return; |
| + |
| + HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| + scoped_ptr<base::DictionaryValue> origin_dict = GetOriginDict( |
| + settings, origin_url); |
| + |
| + if (!origin_dict) |
| + return; |
| + |
| + base::DictionaryValue* app_dict = GetAppDict(origin_dict.get(), app_key); |
| + if (!app_dict) |
| + return; |
| + |
| // Update the setting and save it back. |
| - banner_dict->SetBoolean(sanitized_package_name, false); |
| + app_dict->SetBoolean(kHasBlockedKey, false); |
| settings->SetWebsiteSetting(pattern, |
| ContentSettingsPattern::Wildcard(), |
| CONTENT_SETTINGS_TYPE_APP_BANNER, |
| std::string(), |
| - banner_dict); |
| + origin_dict.get()); |
| } |