OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/web_resource/promo_resource_service.h" | 5 #include "chrome/browser/web_resource/promo_resource_service.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/rand_util.h" | 8 #include "base/message_loop.h" |
9 #include "base/string_number_conversions.h" | |
10 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
11 #include "base/time.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "base/values.h" | 10 #include "base/values.h" |
14 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
15 #include "chrome/browser/extensions/apps_promo.h" | |
16 #include "chrome/browser/prefs/pref_service.h" | 12 #include "chrome/browser/prefs/pref_service.h" |
17 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
18 #include "chrome/browser/sync/sync_ui_util.h" | |
19 #include "chrome/common/chrome_notification_types.h" | 14 #include "chrome/common/chrome_notification_types.h" |
20 #include "chrome/common/chrome_switches.h" | 15 #include "chrome/common/chrome_switches.h" |
21 #include "chrome/common/chrome_version_info.h" | |
22 #include "chrome/common/pref_names.h" | 16 #include "chrome/common/pref_names.h" |
23 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
24 #include "content/public/browser/notification_service.h" | 18 #include "content/public/browser/notification_service.h" |
25 #include "googleurl/src/gurl.h" | 19 #include "googleurl/src/gurl.h" |
26 | 20 |
27 namespace { | 21 namespace { |
28 | 22 |
29 // Delay on first fetch so we don't interfere with startup. | 23 // Delay on first fetch so we don't interfere with startup. |
30 static const int kStartResourceFetchDelay = 5000; | 24 static const int kStartResourceFetchDelay = 5000; |
31 | 25 |
32 // Delay between calls to update the cache (12 hours), and 3 min in debug mode. | 26 // Delay between calls to update the cache (12 hours), and 3 min in debug mode. |
33 static const int kCacheUpdateDelay = 12 * 60 * 60 * 1000; | 27 static const int kCacheUpdateDelay = 12 * 60 * 60 * 1000; |
34 static const int kTestCacheUpdateDelay = 3 * 60 * 1000; | 28 static const int kTestCacheUpdateDelay = 3 * 60 * 1000; |
35 | 29 |
36 // The version of the service (used to expire the cache when upgrading Chrome | 30 // The version of the service (used to expire the cache when upgrading Chrome |
37 // to versions with different types of promos). | 31 // to versions with different types of promos). |
38 static const int kPromoServiceVersion = 7; | 32 static const int kPromoServiceVersion = 7; |
39 | 33 |
40 // Properties used by the server. | |
41 static const char kAnswerIdProperty[] = "answer_id"; | |
42 static const char kWebStoreHeaderProperty[] = "question"; | |
43 static const char kWebStoreButtonProperty[] = "inproduct_target"; | |
44 static const char kWebStoreLinkProperty[] = "inproduct"; | |
45 static const char kWebStoreExpireProperty[] = "tooltip"; | |
46 | |
47 GURL GetPromoResourceURL() { | 34 GURL GetPromoResourceURL() { |
48 const std::string promo_server_url = CommandLine::ForCurrentProcess()-> | 35 const std::string promo_server_url = CommandLine::ForCurrentProcess()-> |
49 GetSwitchValueASCII(switches::kPromoServerURL); | 36 GetSwitchValueASCII(switches::kPromoServerURL); |
50 return promo_server_url.empty() ? | 37 return promo_server_url.empty() ? |
51 NotificationPromo::PromoServerURL() : GURL(promo_server_url); | 38 NotificationPromo::PromoServerURL() : GURL(promo_server_url); |
52 } | 39 } |
53 | 40 |
54 bool IsTest() { | 41 bool IsTest() { |
55 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kPromoServerURL); | 42 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kPromoServerURL); |
56 } | 43 } |
57 | 44 |
58 int GetCacheUpdateDelay() { | 45 int GetCacheUpdateDelay() { |
59 return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay; | 46 return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay; |
60 } | 47 } |
61 | 48 |
62 } // namespace | 49 } // namespace |
63 | 50 |
64 // static | 51 // static |
65 void PromoResourceService::RegisterPrefs(PrefService* local_state) { | 52 void PromoResourceService::RegisterPrefs(PrefService* local_state) { |
66 local_state->RegisterIntegerPref(prefs::kNtpPromoVersion, 0); | 53 local_state->RegisterIntegerPref(prefs::kNtpPromoVersion, 0); |
67 local_state->RegisterStringPref(prefs::kNtpPromoLocale, std::string()); | 54 local_state->RegisterStringPref(prefs::kNtpPromoLocale, std::string()); |
68 } | 55 } |
69 | 56 |
70 // static | 57 // static |
71 void PromoResourceService::RegisterUserPrefs(PrefService* prefs) { | 58 void PromoResourceService::RegisterUserPrefs(PrefService* prefs) { |
72 prefs->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, | 59 prefs->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, |
73 "0", | 60 "0", |
74 PrefService::UNSYNCABLE_PREF); | 61 PrefService::UNSYNCABLE_PREF); |
62 NotificationPromo::RegisterUserPrefs(prefs); | |
63 | |
64 // TODO(achuith): Delete this in M21 | |
Dan Beam
2012/06/08 18:49:47
M21 or M22? . and end.
achuithb
2012/06/08 22:29:08
Will fix.
| |
75 prefs->RegisterDoublePref(prefs::kNtpCustomLogoStart, | 65 prefs->RegisterDoublePref(prefs::kNtpCustomLogoStart, |
76 0, | 66 0, |
77 PrefService::UNSYNCABLE_PREF); | 67 PrefService::UNSYNCABLE_PREF); |
78 prefs->RegisterDoublePref(prefs::kNtpCustomLogoEnd, | 68 prefs->RegisterDoublePref(prefs::kNtpCustomLogoEnd, |
79 0, | 69 0, |
80 PrefService::UNSYNCABLE_PREF); | 70 PrefService::UNSYNCABLE_PREF); |
81 NotificationPromo::RegisterUserPrefs(prefs); | 71 prefs->ClearPref(prefs::kNtpCustomLogoStart); |
82 } | 72 prefs->ClearPref(prefs::kNtpCustomLogoEnd); |
83 | |
84 // static | |
85 chrome::VersionInfo::Channel PromoResourceService::GetChannel() { | |
86 // GetChannel hits the registry on Windows. See http://crbug.com/70898. | |
87 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
88 return chrome::VersionInfo::GetChannel(); | |
89 } | |
90 | |
91 // static | |
92 bool PromoResourceService::IsBuildTargeted(chrome::VersionInfo::Channel channel, | |
93 int builds_allowed) { | |
94 if (builds_allowed == NO_BUILD || | |
95 builds_allowed < 0 || | |
96 builds_allowed > ALL_BUILDS) { | |
97 return false; | |
98 } | |
99 switch (channel) { | |
100 case chrome::VersionInfo::CHANNEL_CANARY: | |
101 return (CANARY_BUILD & builds_allowed) != 0; | |
102 case chrome::VersionInfo::CHANNEL_DEV: | |
103 return (DEV_BUILD & builds_allowed) != 0; | |
104 case chrome::VersionInfo::CHANNEL_BETA: | |
105 return (BETA_BUILD & builds_allowed) != 0; | |
106 case chrome::VersionInfo::CHANNEL_STABLE: | |
107 return (STABLE_BUILD & builds_allowed) != 0; | |
108 default: | |
109 // Show promos for local builds when using a custom promo URL. | |
110 return CommandLine::ForCurrentProcess()->HasSwitch( | |
111 switches::kPromoServerURL); | |
112 } | |
113 } | 73 } |
114 | 74 |
115 PromoResourceService::PromoResourceService(Profile* profile) | 75 PromoResourceService::PromoResourceService(Profile* profile) |
116 : WebResourceService(profile->GetPrefs(), | 76 : WebResourceService(profile->GetPrefs(), |
117 GetPromoResourceURL(), | 77 GetPromoResourceURL(), |
118 true, // append locale to URL | 78 true, // append locale to URL |
119 prefs::kNtpPromoResourceCacheUpdate, | 79 prefs::kNtpPromoResourceCacheUpdate, |
120 kStartResourceFetchDelay, | 80 kStartResourceFetchDelay, |
121 GetCacheUpdateDelay()), | 81 GetCacheUpdateDelay()), |
122 profile_(profile), | 82 profile_(profile), |
123 channel_(chrome::VersionInfo::CHANNEL_UNKNOWN), | 83 ALLOW_THIS_IN_INITIALIZER_LIST( |
124 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), | 84 weak_ptr_factory_(this)), |
125 web_resource_update_scheduled_(false) { | 85 web_resource_update_scheduled_(false) { |
126 ScheduleNotificationOnInit(); | 86 ScheduleNotificationOnInit(); |
127 } | 87 } |
128 | 88 |
129 PromoResourceService::~PromoResourceService() { } | 89 PromoResourceService::~PromoResourceService() { |
130 | |
131 bool PromoResourceService::IsBuildTargeted(int builds_targeted) { | |
132 if (channel_ == chrome::VersionInfo::CHANNEL_UNKNOWN) | |
133 channel_ = GetChannel(); | |
134 | |
135 return IsBuildTargeted(channel_, builds_targeted); | |
136 } | |
137 | |
138 void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { | |
139 UnpackLogoSignal(parsed_json); | |
140 UnpackNotificationSignal(parsed_json); | |
141 UnpackWebStoreSignal(parsed_json); | |
142 } | 90 } |
143 | 91 |
144 void PromoResourceService::ScheduleNotification(double promo_start, | 92 void PromoResourceService::ScheduleNotification(double promo_start, |
145 double promo_end) { | 93 double promo_end) { |
146 if (promo_start > 0 && promo_end > 0) { | 94 if (promo_start > 0 && promo_end > 0) { |
147 int64 ms_until_start = | 95 const int64 ms_until_start = |
148 static_cast<int64>((base::Time::FromDoubleT( | 96 static_cast<int64>((base::Time::FromDoubleT( |
149 promo_start) - base::Time::Now()).InMilliseconds()); | 97 promo_start) - base::Time::Now()).InMilliseconds()); |
150 int64 ms_until_end = | 98 const int64 ms_until_end = |
151 static_cast<int64>((base::Time::FromDoubleT( | 99 static_cast<int64>((base::Time::FromDoubleT( |
152 promo_end) - base::Time::Now()).InMilliseconds()); | 100 promo_end) - base::Time::Now()).InMilliseconds()); |
153 if (ms_until_start > 0) | 101 if (ms_until_start > 0) |
154 PostNotification(ms_until_start); | 102 PostNotification(ms_until_start); |
155 if (ms_until_end > 0) { | 103 if (ms_until_end > 0) { |
156 PostNotification(ms_until_end); | 104 PostNotification(ms_until_end); |
157 if (ms_until_start <= 0) { | 105 if (ms_until_start <= 0) { |
158 // Notify immediately if time is between start and end. | 106 // Notify immediately if time is between start and end. |
159 PostNotification(0); | 107 PostNotification(0); |
160 } | 108 } |
161 } | 109 } |
162 } | 110 } |
163 } | 111 } |
164 | 112 |
165 void PromoResourceService::ScheduleNotificationOnInit() { | 113 void PromoResourceService::ScheduleNotificationOnInit() { |
166 std::string locale = g_browser_process->GetApplicationLocale(); | 114 std::string locale = g_browser_process->GetApplicationLocale(); |
167 if (GetPromoServiceVersion() != kPromoServiceVersion || | 115 if (GetPromoServiceVersion() != kPromoServiceVersion || |
168 GetPromoLocale() != locale) { | 116 GetPromoLocale() != locale) { |
169 // If the promo service has been upgraded or Chrome switched locales, | 117 // If the promo service has been upgraded or Chrome switched locales, |
170 // refresh the promos. | 118 // refresh the promos. |
171 // TODO(achuith): Mixing local_state and prefs does not work for | 119 // TODO(achuith): Mixing local_state and prefs does not work for |
172 // multi-profile case. We should probably store version/locale in prefs_ | 120 // multi-profile case. We should probably store version/locale in prefs_ |
173 // as well. | 121 // as well. |
174 PrefService* local_state = g_browser_process->local_state(); | 122 PrefService* local_state = g_browser_process->local_state(); |
175 local_state->SetInteger(prefs::kNtpPromoVersion, kPromoServiceVersion); | 123 local_state->SetInteger(prefs::kNtpPromoVersion, kPromoServiceVersion); |
176 local_state->SetString(prefs::kNtpPromoLocale, locale); | 124 local_state->SetString(prefs::kNtpPromoLocale, locale); |
177 prefs_->ClearPref(prefs::kNtpPromoResourceCacheUpdate); | 125 prefs_->ClearPref(prefs::kNtpPromoResourceCacheUpdate); |
178 AppsPromo::ClearPromo(); | |
179 PostNotification(0); | 126 PostNotification(0); |
180 } else { | 127 } else { |
181 // If the promo start is in the future, set a notification task to | 128 // If the promo start is in the future, set a notification task to |
182 // invalidate the NTP cache at the time of the promo start. | 129 // invalidate the NTP cache at the time of the promo start. |
183 double promo_start = prefs_->GetDouble(prefs::kNtpPromoStart); | 130 double promo_start = prefs_->GetDouble(prefs::kNtpPromoStart); |
184 double promo_end = prefs_->GetDouble(prefs::kNtpPromoEnd); | 131 double promo_end = prefs_->GetDouble(prefs::kNtpPromoEnd); |
185 ScheduleNotification(promo_start, promo_end); | 132 ScheduleNotification(promo_start, promo_end); |
186 } | 133 } |
187 } | 134 } |
188 | 135 |
(...skipping 26 matching lines...) Expand all Loading... | |
215 int PromoResourceService::GetPromoServiceVersion() { | 162 int PromoResourceService::GetPromoServiceVersion() { |
216 PrefService* local_state = g_browser_process->local_state(); | 163 PrefService* local_state = g_browser_process->local_state(); |
217 return local_state->GetInteger(prefs::kNtpPromoVersion); | 164 return local_state->GetInteger(prefs::kNtpPromoVersion); |
218 } | 165 } |
219 | 166 |
220 std::string PromoResourceService::GetPromoLocale() { | 167 std::string PromoResourceService::GetPromoLocale() { |
221 PrefService* local_state = g_browser_process->local_state(); | 168 PrefService* local_state = g_browser_process->local_state(); |
222 return local_state->GetString(prefs::kNtpPromoLocale); | 169 return local_state->GetString(prefs::kNtpPromoLocale); |
223 } | 170 } |
224 | 171 |
225 void PromoResourceService::UnpackNotificationSignal( | 172 void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { |
226 const DictionaryValue& parsed_json) { | |
227 NotificationPromo notification_promo(profile_); | 173 NotificationPromo notification_promo(profile_); |
228 notification_promo.InitFromJson(parsed_json); | 174 notification_promo.InitFromJson(parsed_json); |
229 | 175 |
230 if (notification_promo.new_notification()) { | 176 if (notification_promo.new_notification()) { |
231 ScheduleNotification(notification_promo.StartTimeForGroup(), | 177 ScheduleNotification(notification_promo.StartTimeForGroup(), |
232 notification_promo.EndTime()); | 178 notification_promo.EndTime()); |
233 } | 179 } |
234 } | 180 } |
235 | 181 |
236 bool PromoResourceService::CanShowNotificationPromo(Profile* profile) { | 182 bool PromoResourceService::CanShowNotificationPromo(Profile* profile) { |
237 NotificationPromo notification_promo(profile); | 183 NotificationPromo notification_promo(profile); |
238 notification_promo.InitFromPrefs(); | 184 notification_promo.InitFromPrefs(); |
239 return notification_promo.CanShow(); | 185 return notification_promo.CanShow(); |
240 } | 186 } |
241 | |
242 void PromoResourceService::UnpackWebStoreSignal( | |
243 const DictionaryValue& parsed_json) { | |
244 DictionaryValue* topic_dict; | |
245 ListValue* answer_list; | |
246 | |
247 bool is_webstore_active = false; | |
248 bool signal_found = false; | |
249 AppsPromo::PromoData promo_data; | |
250 std::string promo_link = ""; | |
251 std::string promo_logo = ""; | |
252 int target_builds = 0; | |
253 | |
254 if (!parsed_json.GetDictionary("topic", &topic_dict) || | |
255 !topic_dict->GetList("answers", &answer_list)) | |
256 return; | |
257 | |
258 for (ListValue::const_iterator answer_iter = answer_list->begin(); | |
259 answer_iter != answer_list->end(); ++answer_iter) { | |
260 if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY)) | |
261 continue; | |
262 DictionaryValue* a_dic = | |
263 static_cast<DictionaryValue*>(*answer_iter); | |
264 | |
265 // The "name" field has three different values packed into it, each | |
266 // separated by a ':'. | |
267 std::string name; | |
268 if (!a_dic->GetString("name", &name)) | |
269 continue; | |
270 | |
271 // (1) the string "webstore_promo" | |
272 size_t split = name.find(":"); | |
273 if (split == std::string::npos || name.substr(0, split) != "webstore_promo") | |
274 continue; | |
275 | |
276 // If the "webstore_promo" string was found, that's enough to activate the | |
277 // apps section even if the rest of the promo fails parsing. | |
278 is_webstore_active = true; | |
279 | |
280 // (2) an integer specifying which builds the promo targets | |
281 name = name.substr(split+1); | |
282 split = name.find(':'); | |
283 if (split == std::string::npos || | |
284 !base::StringToInt(name.substr(0, split), &target_builds)) | |
285 continue; | |
286 | |
287 // (3) an integer specifying what users should maximize the promo | |
288 name = name.substr(split+1); | |
289 split = name.find(':'); | |
290 if (split == std::string::npos || | |
291 !base::StringToInt(name.substr(0, split), &promo_data.user_group)) | |
292 continue; | |
293 | |
294 // (4) optional text that specifies a URL of a logo image | |
295 promo_logo = name.substr(split+1); | |
296 | |
297 if (!a_dic->GetString(kAnswerIdProperty, &promo_data.id) || | |
298 !a_dic->GetString(kWebStoreHeaderProperty, &promo_data.header) || | |
299 !a_dic->GetString(kWebStoreButtonProperty, &promo_data.button) || | |
300 !a_dic->GetString(kWebStoreLinkProperty, &promo_link) || | |
301 !a_dic->GetString(kWebStoreExpireProperty, &promo_data.expire)) | |
302 continue; | |
303 | |
304 if (IsBuildTargeted(target_builds)) { | |
305 // The downloader will set the promo prefs and send the | |
306 // NOTIFICATION_WEB_STORE_PROMO_LOADED notification. | |
307 promo_data.link = GURL(promo_link); | |
308 promo_data.logo = GURL(promo_logo); | |
309 apps_promo_logo_fetcher_.reset( | |
310 new AppsPromoLogoFetcher(profile_, promo_data)); | |
311 signal_found = true; | |
312 break; | |
313 } | |
314 } | |
315 | |
316 if (!signal_found) { | |
317 // If no web store promos target this build, then clear all the prefs. | |
318 AppsPromo::ClearPromo(); | |
319 } | |
320 | |
321 AppsPromo::SetWebStoreSupportedForLocale(is_webstore_active); | |
322 | |
323 return; | |
324 } | |
325 | |
326 void PromoResourceService::UnpackLogoSignal( | |
327 const DictionaryValue& parsed_json) { | |
328 DictionaryValue* topic_dict; | |
329 ListValue* answer_list; | |
330 double old_logo_start = 0; | |
331 double old_logo_end = 0; | |
332 double logo_start = 0; | |
333 double logo_end = 0; | |
334 | |
335 // Check for preexisting start and end values. | |
336 if (prefs_->HasPrefPath(prefs::kNtpCustomLogoStart) && | |
337 prefs_->HasPrefPath(prefs::kNtpCustomLogoEnd)) { | |
338 old_logo_start = prefs_->GetDouble(prefs::kNtpCustomLogoStart); | |
339 old_logo_end = prefs_->GetDouble(prefs::kNtpCustomLogoEnd); | |
340 } | |
341 | |
342 // Check for newly received start and end values. | |
343 if (parsed_json.GetDictionary("topic", &topic_dict)) { | |
344 if (topic_dict->GetList("answers", &answer_list)) { | |
345 std::string logo_start_string = ""; | |
346 std::string logo_end_string = ""; | |
347 for (ListValue::const_iterator answer_iter = answer_list->begin(); | |
348 answer_iter != answer_list->end(); ++answer_iter) { | |
349 if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY)) | |
350 continue; | |
351 DictionaryValue* a_dic = | |
352 static_cast<DictionaryValue*>(*answer_iter); | |
353 std::string logo_signal; | |
354 if (a_dic->GetString("name", &logo_signal)) { | |
355 if (logo_signal == "custom_logo_start") { | |
356 a_dic->GetString("inproduct", &logo_start_string); | |
357 } else if (logo_signal == "custom_logo_end") { | |
358 a_dic->GetString("inproduct", &logo_end_string); | |
359 } | |
360 } | |
361 } | |
362 if (!logo_start_string.empty() && | |
363 logo_start_string.length() > 0 && | |
364 !logo_end_string.empty() && | |
365 logo_end_string.length() > 0) { | |
366 base::Time start_time; | |
367 base::Time end_time; | |
368 if (base::Time::FromString(logo_start_string.c_str(), &start_time) && | |
369 base::Time::FromString(logo_end_string.c_str(), &end_time)) { | |
370 logo_start = start_time.ToDoubleT(); | |
371 logo_end = end_time.ToDoubleT(); | |
372 } | |
373 } | |
374 } | |
375 } | |
376 | |
377 // If logo start or end times have changed, trigger a new web resource | |
378 // notification, so that the logo on the NTP is updated. This check is | |
379 // outside the reading of the web resource data, because the absence of | |
380 // dates counts as a triggering change if there were dates before. | |
381 if (!(old_logo_start == logo_start) || | |
382 !(old_logo_end == logo_end)) { | |
383 prefs_->SetDouble(prefs::kNtpCustomLogoStart, logo_start); | |
384 prefs_->SetDouble(prefs::kNtpCustomLogoEnd, logo_end); | |
385 content::NotificationService* service = | |
386 content::NotificationService::current(); | |
387 service->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED, | |
388 content::Source<WebResourceService>(this), | |
389 content::NotificationService::NoDetails()); | |
390 } | |
391 } | |
OLD | NEW |