Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/banners/app_banner_settings_helper.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "chrome/browser/profiles/profile.h" | |
| 11 #include "components/content_settings/core/browser/host_content_settings_map.h" | |
| 12 #include "components/content_settings/core/common/content_settings_pattern.h" | |
| 13 #include "content/public/browser/web_contents.h" | |
| 14 #include "net/base/escape.h" | |
| 15 #include "url/gurl.h" | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 // Max number of apps (including ServiceWorker based web apps) that a particular | |
| 20 // site may show a banner for. | |
| 21 const size_t kMaxAppsPerSite = 3; | |
| 22 | |
| 23 // Oldest could show banner event we care about, in days. | |
| 24 const unsigned int kOldestCouldShowBannerEventInDays = 14; | |
| 25 | |
| 26 // Dictionary key to use for the 'could show banner' events. | |
| 27 const char kCouldShowBannerEventsKey[] = "couldShowBannerEvents"; | |
| 28 | |
| 29 // Dictionary key to use whether the banner has been blocked. | |
| 30 const char kHasBlockedKey[] = "hasBlocked"; | |
| 31 | |
| 32 base::Time DateFromTime(base::Time time) { | |
| 33 base::Time::Exploded exploded; | |
| 34 time.LocalExplode(&exploded); | |
| 35 exploded.hour = 0; | |
| 36 exploded.minute = 0; | |
| 37 exploded.second = 0; | |
| 38 return base::Time::FromLocalExploded(exploded); | |
| 39 } | |
| 40 | |
| 41 scoped_ptr<base::DictionaryValue> GetOriginDict( | |
| 42 HostContentSettingsMap* settings, | |
| 43 const GURL& origin_url) { | |
| 44 if (!settings) | |
| 45 return scoped_ptr<base::DictionaryValue>(); | |
| 46 | |
| 47 scoped_ptr<base::Value> value = settings->GetWebsiteSetting( | |
| 48 origin_url, origin_url, CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), | |
| 49 NULL); | |
| 50 if (!value.get()) | |
| 51 return make_scoped_ptr(new base::DictionaryValue()); | |
| 52 | |
| 53 if (!value->IsType(base::Value::TYPE_DICTIONARY)) | |
| 54 return make_scoped_ptr(new base::DictionaryValue()); | |
| 55 | |
| 56 return make_scoped_ptr(static_cast<base::DictionaryValue*>(value.release())); | |
| 57 } | |
| 58 | |
| 59 base::DictionaryValue* GetAppDict(base::DictionaryValue* origin_dict, | |
| 60 const std::string& key_name) { | |
| 61 base::DictionaryValue* app_dict = nullptr; | |
| 62 if (!origin_dict->GetDictionaryWithoutPathExpansion(key_name, &app_dict)) { | |
| 63 // Don't allow more than kMaxAppsPerSite dictionaries. | |
| 64 if (origin_dict->size() < kMaxAppsPerSite) { | |
| 65 app_dict = new base::DictionaryValue(); | |
| 66 origin_dict->SetWithoutPathExpansion(key_name, make_scoped_ptr(app_dict)); | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 return app_dict; | |
| 71 } | |
| 72 | |
| 73 } // namespace | |
| 74 | |
| 75 void AppBannerSettingsHelper::RecordCouldShowBannerEvent( | |
| 76 content::WebContents* web_contents, | |
| 77 const GURL& origin_url, | |
| 78 const std::string& package_name_or_start_url, | |
| 79 base::Time time) { | |
| 80 Profile* profile = | |
| 81 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 82 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
| 83 package_name_or_start_url.empty()) { | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); | |
| 88 if (!pattern.IsValid()) | |
| 89 return; | |
| 90 | |
| 91 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
| 92 scoped_ptr<base::DictionaryValue> origin_dict = | |
| 93 GetOriginDict(settings, origin_url); | |
| 94 if (!origin_dict) | |
| 95 return; | |
| 96 | |
| 97 base::DictionaryValue* app_dict = | |
| 98 GetAppDict(origin_dict.get(), package_name_or_start_url); | |
| 99 if (!app_dict) | |
| 100 return; | |
| 101 | |
| 102 base::ListValue* could_show_list = nullptr; | |
| 103 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) { | |
| 104 could_show_list = new base::ListValue(); | |
| 105 app_dict->Set(kCouldShowBannerEventsKey, make_scoped_ptr(could_show_list)); | |
| 106 } | |
| 107 | |
| 108 // Trim any items that are older than we should care about. | |
| 109 base::Time date = DateFromTime(time); | |
| 110 base::ValueVector::iterator it = could_show_list->begin(); | |
| 111 while (it != could_show_list->end()) { | |
| 112 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) { | |
| 113 double internal_date; | |
| 114 (*it)->GetAsDouble(&internal_date); | |
| 115 base::Time other_date = base::Time::FromInternalValue(internal_date); | |
| 116 // This date has already been added. Don't add the date again, and don't | |
| 117 // bother trimming values as it will have been done the first time the | |
| 118 // date was added (unless the local date has changed, which we can live | |
| 119 // with). | |
| 120 if (other_date == date) | |
|
Bernhard Bauer
2015/01/30 13:25:25
Another problematic edge case: If the value from c
benwells
2015/01/30 20:47:28
Good idea. Done.
| |
| 121 return; | |
| 122 | |
| 123 base::TimeDelta delta = date - other_date; | |
| 124 if (delta < | |
| 125 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) { | |
| 126 ++it; | |
| 127 continue; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 // Either this date is older than we care about, or it isn't a date, so | |
| 132 // remove it; | |
| 133 it = could_show_list->Erase(it, nullptr); | |
| 134 } | |
| 135 | |
| 136 could_show_list->AppendDouble(date.ToInternalValue()); | |
| 137 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), | |
| 138 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), | |
| 139 origin_dict.release()); | |
| 140 } | |
| 141 | |
| 142 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents( | |
| 143 content::WebContents* web_contents, | |
| 144 const GURL& origin_url, | |
| 145 const std::string& package_name_or_start_url) { | |
| 146 std::vector<base::Time> result; | |
| 147 if (package_name_or_start_url.empty()) | |
| 148 return result; | |
| 149 | |
| 150 Profile* profile = | |
| 151 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 152 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
| 153 scoped_ptr<base::DictionaryValue> origin_dict = | |
| 154 GetOriginDict(settings, origin_url); | |
| 155 | |
| 156 if (!origin_dict) | |
| 157 return result; | |
| 158 | |
| 159 base::DictionaryValue* app_dict = | |
| 160 GetAppDict(origin_dict.get(), package_name_or_start_url); | |
| 161 if (!app_dict) | |
| 162 return result; | |
| 163 | |
| 164 base::ListValue* could_show_list = nullptr; | |
| 165 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) | |
| 166 return result; | |
| 167 | |
| 168 for (base::ValueVector::iterator it = could_show_list->begin(); | |
|
Bernhard Bauer
2015/01/30 13:25:26
Use a C++11-style for-loop?
benwells
2015/01/30 20:47:28
Done.
| |
| 169 it != could_show_list->end(); ++it) { | |
| 170 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) { | |
| 171 double internal_date; | |
| 172 (*it)->GetAsDouble(&internal_date); | |
| 173 base::Time date = base::Time::FromInternalValue(internal_date); | |
| 174 result.push_back(date); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 return result; | |
| 179 } | |
| 180 | |
| 181 bool AppBannerSettingsHelper::IsAllowed( | |
| 182 content::WebContents* web_contents, | |
| 183 const GURL& origin_url, | |
| 184 const std::string& package_name_or_start_url) { | |
| 185 Profile* profile = | |
| 186 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 187 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
| 188 package_name_or_start_url.empty()) { | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
| 193 scoped_ptr<base::DictionaryValue> origin_dict = | |
| 194 GetOriginDict(settings, origin_url); | |
| 195 | |
| 196 if (!origin_dict) | |
| 197 return true; | |
| 198 | |
| 199 base::DictionaryValue* app_dict = | |
| 200 GetAppDict(origin_dict.get(), package_name_or_start_url); | |
| 201 if (!app_dict) | |
| 202 return true; | |
| 203 | |
| 204 bool has_blocked; | |
| 205 if (!app_dict->GetBoolean(kHasBlockedKey, &has_blocked)) | |
| 206 return true; | |
| 207 | |
| 208 return !has_blocked; | |
| 209 } | |
| 210 | |
| 211 void AppBannerSettingsHelper::Block( | |
| 212 content::WebContents* web_contents, | |
| 213 const GURL& origin_url, | |
| 214 const std::string& package_name_or_start_url) { | |
| 215 Profile* profile = | |
| 216 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 217 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || | |
| 218 package_name_or_start_url.empty()) { | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); | |
| 223 if (!pattern.IsValid()) | |
| 224 return; | |
| 225 | |
| 226 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); | |
| 227 scoped_ptr<base::DictionaryValue> origin_dict = | |
| 228 GetOriginDict(settings, origin_url); | |
| 229 | |
| 230 if (!origin_dict) | |
| 231 return; | |
| 232 | |
| 233 base::DictionaryValue* app_dict = | |
| 234 GetAppDict(origin_dict.get(), package_name_or_start_url); | |
| 235 if (!app_dict) | |
| 236 return; | |
| 237 | |
| 238 // Update the setting and save it back. | |
| 239 app_dict->SetBoolean(kHasBlockedKey, true); | |
| 240 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), | |
| 241 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), | |
| 242 origin_dict.release()); | |
| 243 } | |
| OLD | NEW |