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. For comparisons |
| 109 // the times are converted to local dates. |
| 110 base::Time date = DateFromTime(time); |
| 111 base::ValueVector::iterator it = could_show_list->begin(); |
| 112 while (it != could_show_list->end()) { |
| 113 if ((*it)->IsType(base::Value::TYPE_DOUBLE)) { |
| 114 double internal_date; |
| 115 (*it)->GetAsDouble(&internal_date); |
| 116 base::Time other_date = |
| 117 DateFromTime(base::Time::FromInternalValue(internal_date)); |
| 118 // This date has already been added. Don't add the date again, and don't |
| 119 // bother trimming values as it will have been done the first time the |
| 120 // date was added (unless the local date has changed, which we can live |
| 121 // with). |
| 122 if (other_date == date) |
| 123 return; |
| 124 |
| 125 base::TimeDelta delta = date - other_date; |
| 126 if (delta < |
| 127 base::TimeDelta::FromDays(kOldestCouldShowBannerEventInDays)) { |
| 128 ++it; |
| 129 continue; |
| 130 } |
| 131 } |
| 132 |
| 133 // Either this date is older than we care about, or it isn't a date, so |
| 134 // remove it; |
| 135 it = could_show_list->Erase(it, nullptr); |
| 136 } |
| 137 |
| 138 // Dates are stored in their raw form (i.e. not local dates) to be resilient |
| 139 // to time zone changes. |
| 140 could_show_list->AppendDouble(time.ToInternalValue()); |
| 141 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), |
| 142 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), |
| 143 origin_dict.release()); |
| 144 } |
| 145 |
| 146 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents( |
| 147 content::WebContents* web_contents, |
| 148 const GURL& origin_url, |
| 149 const std::string& package_name_or_start_url) { |
| 150 std::vector<base::Time> result; |
| 151 if (package_name_or_start_url.empty()) |
| 152 return result; |
| 153 |
| 154 Profile* profile = |
| 155 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 156 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| 157 scoped_ptr<base::DictionaryValue> origin_dict = |
| 158 GetOriginDict(settings, origin_url); |
| 159 |
| 160 if (!origin_dict) |
| 161 return result; |
| 162 |
| 163 base::DictionaryValue* app_dict = |
| 164 GetAppDict(origin_dict.get(), package_name_or_start_url); |
| 165 if (!app_dict) |
| 166 return result; |
| 167 |
| 168 base::ListValue* could_show_list = nullptr; |
| 169 if (!app_dict->GetList(kCouldShowBannerEventsKey, &could_show_list)) |
| 170 return result; |
| 171 |
| 172 for (auto value : *could_show_list) { |
| 173 if (value->IsType(base::Value::TYPE_DOUBLE)) { |
| 174 double internal_date; |
| 175 value->GetAsDouble(&internal_date); |
| 176 base::Time date = base::Time::FromInternalValue(internal_date); |
| 177 result.push_back(date); |
| 178 } |
| 179 } |
| 180 |
| 181 return result; |
| 182 } |
| 183 |
| 184 bool AppBannerSettingsHelper::IsAllowed( |
| 185 content::WebContents* web_contents, |
| 186 const GURL& origin_url, |
| 187 const std::string& package_name_or_start_url) { |
| 188 Profile* profile = |
| 189 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 190 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| 191 package_name_or_start_url.empty()) { |
| 192 return false; |
| 193 } |
| 194 |
| 195 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| 196 scoped_ptr<base::DictionaryValue> origin_dict = |
| 197 GetOriginDict(settings, origin_url); |
| 198 |
| 199 if (!origin_dict) |
| 200 return true; |
| 201 |
| 202 base::DictionaryValue* app_dict = |
| 203 GetAppDict(origin_dict.get(), package_name_or_start_url); |
| 204 if (!app_dict) |
| 205 return true; |
| 206 |
| 207 bool has_blocked; |
| 208 if (!app_dict->GetBoolean(kHasBlockedKey, &has_blocked)) |
| 209 return true; |
| 210 |
| 211 return !has_blocked; |
| 212 } |
| 213 |
| 214 void AppBannerSettingsHelper::Block( |
| 215 content::WebContents* web_contents, |
| 216 const GURL& origin_url, |
| 217 const std::string& package_name_or_start_url) { |
| 218 Profile* profile = |
| 219 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 220 if (profile->IsOffTheRecord() || web_contents->GetURL() != origin_url || |
| 221 package_name_or_start_url.empty()) { |
| 222 return; |
| 223 } |
| 224 |
| 225 ContentSettingsPattern pattern(ContentSettingsPattern::FromURL(origin_url)); |
| 226 if (!pattern.IsValid()) |
| 227 return; |
| 228 |
| 229 HostContentSettingsMap* settings = profile->GetHostContentSettingsMap(); |
| 230 scoped_ptr<base::DictionaryValue> origin_dict = |
| 231 GetOriginDict(settings, origin_url); |
| 232 |
| 233 if (!origin_dict) |
| 234 return; |
| 235 |
| 236 base::DictionaryValue* app_dict = |
| 237 GetAppDict(origin_dict.get(), package_name_or_start_url); |
| 238 if (!app_dict) |
| 239 return; |
| 240 |
| 241 // Update the setting and save it back. |
| 242 app_dict->SetBoolean(kHasBlockedKey, true); |
| 243 settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(), |
| 244 CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(), |
| 245 origin_dict.release()); |
| 246 } |
OLD | NEW |