Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/string_number_conversions.h" | 7 #include "base/string_number_conversions.h" |
| 8 #include "base/threading/thread_restrictions.h" | 8 #include "base/threading/thread_restrictions.h" |
| 9 #include "base/time.h" | 9 #include "base/time.h" |
| 10 #include "base/values.h" | 10 #include "base/values.h" |
| 11 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
| 12 #include "chrome/browser/extensions/apps_promo.h" | |
| 12 #include "chrome/browser/platform_util.h" | 13 #include "chrome/browser/platform_util.h" |
| 13 #include "chrome/browser/prefs/pref_service.h" | 14 #include "chrome/browser/prefs/pref_service.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/browser/sync/sync_ui_util.h" | 16 #include "chrome/browser/sync/sync_ui_util.h" |
| 16 #include "chrome/common/pref_names.h" | 17 #include "chrome/common/pref_names.h" |
| 17 #include "content/browser/browser_thread.h" | 18 #include "content/browser/browser_thread.h" |
| 18 #include "content/common/notification_service.h" | 19 #include "content/common/notification_service.h" |
| 19 #include "content/common/notification_type.h" | 20 #include "content/common/notification_type.h" |
| 21 #include "googleurl/src/gurl.h" | |
| 20 | 22 |
| 21 namespace { | 23 namespace { |
| 22 | 24 |
| 23 // Delay on first fetch so we don't interfere with startup. | 25 // Delay on first fetch so we don't interfere with startup. |
| 24 static const int kStartResourceFetchDelay = 5000; | 26 static const int kStartResourceFetchDelay = 5000; |
| 25 | 27 |
| 26 // Delay between calls to update the cache (48 hours). | 28 // Delay between calls to update the cache (48 hours). |
| 27 static const int kCacheUpdateDelay = 48 * 60 * 60 * 1000; | 29 static const int kCacheUpdateDelay = 48 * 60 * 60 * 1000; |
| 28 | 30 |
| 29 // Users are randomly assigned to one of kNTPPromoGroupSize buckets, in order | 31 // Users are randomly assigned to one of kNTPPromoGroupSize buckets, in order |
| 30 // to be able to roll out promos slowly, or display different promos to | 32 // to be able to roll out promos slowly, or display different promos to |
| 31 // different groups. | 33 // different groups. |
| 32 static const int kNTPPromoGroupSize = 16; | 34 static const int kNTPPromoGroupSize = 16; |
| 33 | 35 |
| 34 // Maximum number of hours for each time slice (4 weeks). | 36 // Maximum number of hours for each time slice (4 weeks). |
| 35 static const int kMaxTimeSliceHours = 24 * 7 * 4; | 37 static const int kMaxTimeSliceHours = 24 * 7 * 4; |
| 36 | 38 |
| 37 // Used to determine which build type should be shown a given promo. | 39 // The version of the service (used to expire the cache when upgrading Chrome |
| 38 enum BuildType { | 40 // to versions with different types of promos). |
| 39 NO_BUILD = 0, | 41 static const int kPromoServiceVersion = 1; |
| 40 DEV_BUILD = 1, | 42 |
| 41 BETA_BUILD = 1 << 1, | 43 // Properties used by the server. |
| 42 STABLE_BUILD = 1 << 2, | 44 static const char kAnswerIdProperty[] = "answer_id"; |
| 43 }; | 45 static const char kWebStoreHeaderProperty[] = "answer1"; |
| 46 static const char kWebStoreButtonProperty[] = "answer2"; | |
| 47 static const char kWebStoreLinkProperty[] = "answer3"; | |
| 48 static const char kWebStoreExpireProperty[] = "answer4"; | |
| 49 static const char kWebStoreBuildsProperty[] = "inproduct"; | |
| 44 | 50 |
| 45 } // namespace | 51 } // namespace |
| 46 | 52 |
| 47 // Server for dynamically loaded NTP HTML elements. TODO(mirandac): append | 53 // Server for dynamically loaded NTP HTML elements. TODO(mirandac): append |
| 48 // locale for future usage, when we're serving localizable strings. | 54 // locale for future usage, when we're serving localizable strings. |
| 49 const char* PromoResourceService::kDefaultPromoResourceServer = | 55 const char* PromoResourceService::kDefaultPromoResourceServer = |
| 50 "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl="; | 56 "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl="; |
| 51 | 57 |
| 58 // static | |
| 59 void PromoResourceService::RegisterPrefs(PrefService* local_state) { | |
| 60 local_state->RegisterIntegerPref(prefs::kNTPPromoVersion, 0); | |
| 61 local_state->RegisterStringPref(prefs::kNTPPromoLocale, std::string()); | |
| 62 } | |
| 63 | |
| 64 // static | |
| 65 void PromoResourceService::RegisterUserPrefs(PrefService* prefs) { | |
| 66 prefs->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0); | |
| 67 prefs->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0); | |
| 68 prefs->RegisterDoublePref(prefs::kNTPPromoStart, 0); | |
| 69 prefs->RegisterDoublePref(prefs::kNTPPromoEnd, 0); | |
| 70 prefs->RegisterStringPref(prefs::kNTPPromoLine, std::string()); | |
| 71 prefs->RegisterBooleanPref(prefs::kNTPPromoClosed, false); | |
| 72 prefs->RegisterIntegerPref(prefs::kNTPPromoGroup, -1); | |
| 73 prefs->RegisterIntegerPref(prefs::kNTPPromoBuild, | |
| 74 CANARY_BUILD | DEV_BUILD | BETA_BUILD | STABLE_BUILD); | |
| 75 prefs->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0); | |
| 76 } | |
| 77 | |
| 78 // static | |
| 79 bool PromoResourceService::IsBuildTargeted(const std::string& channel, | |
| 80 int builds_allowed) { | |
| 81 if (builds_allowed == NO_BUILD) | |
| 82 return false; | |
| 83 if (channel == "canary" || channel == "canary-m") { | |
| 84 return (CANARY_BUILD & builds_allowed) != 0; | |
| 85 } else if (channel == "dev" || channel == "dev-m") { | |
| 86 return (DEV_BUILD & builds_allowed) != 0; | |
| 87 } else if (channel == "beta" || channel == "beta-m") { | |
| 88 return (BETA_BUILD & builds_allowed) != 0; | |
| 89 } else if (channel == "" || channel == "m") { | |
| 90 return (STABLE_BUILD & builds_allowed) != 0; | |
| 91 } else { | |
| 92 return false; | |
| 93 } | |
| 94 } | |
| 95 | |
| 52 PromoResourceService::PromoResourceService(Profile* profile) | 96 PromoResourceService::PromoResourceService(Profile* profile) |
| 53 : WebResourceService(profile, | 97 : WebResourceService(profile, |
| 54 profile->GetPrefs(), | 98 profile->GetPrefs(), |
| 55 PromoResourceService::kDefaultPromoResourceServer, | 99 PromoResourceService::kDefaultPromoResourceServer, |
| 56 true, // append locale to URL | 100 true, // append locale to URL |
| 57 NotificationType::PROMO_RESOURCE_STATE_CHANGED, | 101 NotificationType::PROMO_RESOURCE_STATE_CHANGED, |
| 58 prefs::kNTPPromoResourceCacheUpdate, | 102 prefs::kNTPPromoResourceCacheUpdate, |
| 59 kStartResourceFetchDelay, | 103 kStartResourceFetchDelay, |
| 60 kCacheUpdateDelay), | 104 kCacheUpdateDelay), |
| 61 web_resource_cache_(NULL) { | 105 web_resource_cache_(NULL), |
| 106 channel_(NULL) { | |
| 62 Init(); | 107 Init(); |
| 63 } | 108 } |
| 64 | 109 |
| 65 PromoResourceService::~PromoResourceService() { } | 110 PromoResourceService::~PromoResourceService() { } |
| 66 | 111 |
| 67 void PromoResourceService::Init() { | 112 void PromoResourceService::Init() { |
| 68 prefs_->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0); | 113 ScheduleNotificationOnInit(); |
| 69 prefs_->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0); | 114 } |
| 70 prefs_->RegisterDoublePref(prefs::kNTPPromoStart, 0); | |
| 71 prefs_->RegisterDoublePref(prefs::kNTPPromoEnd, 0); | |
| 72 prefs_->RegisterStringPref(prefs::kNTPPromoLine, std::string()); | |
| 73 prefs_->RegisterBooleanPref(prefs::kNTPPromoClosed, false); | |
| 74 prefs_->RegisterIntegerPref(prefs::kNTPPromoGroup, -1); | |
| 75 prefs_->RegisterIntegerPref(prefs::kNTPPromoBuild, | |
| 76 DEV_BUILD | BETA_BUILD | STABLE_BUILD); | |
| 77 prefs_->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0); | |
| 78 | 115 |
| 79 // If the promo start is in the future, set a notification task to invalidate | 116 bool PromoResourceService::IsThisBuildTargeted(int builds_targeted) { |
| 80 // the NTP cache at the time of the promo start. | 117 std::string channel; |
| 81 double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart); | 118 if (channel_ != NULL) { |
| 82 double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd); | 119 channel = channel_; |
| 83 ScheduleNotification(promo_start, promo_end); | 120 } else { |
| 121 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 122 channel = platform_util::GetVersionStringModifier(); | |
|
Miranda Callahan
2011/04/13 14:29:29
did you want to cache the channel in channel_ here
jstritar
2011/04/13 19:52:27
Oh, yeah! Done.
| |
| 123 } | |
| 124 | |
| 125 return IsBuildTargeted(channel, builds_targeted); | |
| 84 } | 126 } |
| 85 | 127 |
| 86 void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { | 128 void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { |
| 87 UnpackLogoSignal(parsed_json); | 129 UnpackLogoSignal(parsed_json); |
| 88 UnpackPromoSignal(parsed_json); | 130 UnpackPromoSignal(parsed_json); |
| 131 UnpackWebStoreSignal(parsed_json); | |
| 89 } | 132 } |
| 90 | 133 |
| 91 void PromoResourceService::ScheduleNotification(double promo_start, | 134 void PromoResourceService::ScheduleNotification(double promo_start, |
| 92 double promo_end) { | 135 double promo_end) { |
| 93 if (promo_start > 0 && promo_end > 0) { | 136 if (promo_start > 0 && promo_end > 0) { |
| 94 int64 ms_until_start = | 137 int64 ms_until_start = |
| 95 static_cast<int64>((base::Time::FromDoubleT( | 138 static_cast<int64>((base::Time::FromDoubleT( |
| 96 promo_start) - base::Time::Now()).InMilliseconds()); | 139 promo_start) - base::Time::Now()).InMilliseconds()); |
| 97 int64 ms_until_end = | 140 int64 ms_until_end = |
| 98 static_cast<int64>((base::Time::FromDoubleT( | 141 static_cast<int64>((base::Time::FromDoubleT( |
| 99 promo_end) - base::Time::Now()).InMilliseconds()); | 142 promo_end) - base::Time::Now()).InMilliseconds()); |
| 100 if (ms_until_start > 0) | 143 if (ms_until_start > 0) |
| 101 PostNotification(ms_until_start); | 144 PostNotification(ms_until_start); |
| 102 if (ms_until_end > 0) { | 145 if (ms_until_end > 0) { |
| 103 PostNotification(ms_until_end); | 146 PostNotification(ms_until_end); |
| 104 if (ms_until_start <= 0) { | 147 if (ms_until_start <= 0) { |
| 105 // Notify immediately if time is between start and end. | 148 // Notify immediately if time is between start and end. |
| 106 PostNotification(0); | 149 PostNotification(0); |
| 107 } | 150 } |
| 108 } | 151 } |
| 109 } | 152 } |
| 110 } | 153 } |
| 111 | 154 |
| 155 void PromoResourceService::ScheduleNotificationOnInit() { | |
| 156 std::string locale = g_browser_process->GetApplicationLocale(); | |
| 157 if ((GetPromoServiceVersion() != kPromoServiceVersion) || | |
| 158 (GetPromoLocale() != locale)) { | |
| 159 // If the promo service has been upgraded or Chrome switched locales, | |
| 160 // refresh the promos. | |
| 161 PrefService* local_state = g_browser_process->local_state(); | |
| 162 local_state->SetInteger(prefs::kNTPPromoVersion, kPromoServiceVersion); | |
| 163 local_state->SetString(prefs::kNTPPromoLocale, locale); | |
| 164 prefs_->ClearPref(prefs::kNTPPromoResourceCacheUpdate); | |
| 165 AppsPromo::ClearPromo(); | |
| 166 PostNotification(0); | |
| 167 } else { | |
| 168 // If the promo start is in the future, set a notification task to | |
| 169 // invalidate the NTP cache at the time of the promo start. | |
| 170 double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart); | |
| 171 double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd); | |
| 172 ScheduleNotification(promo_start, promo_end); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 int PromoResourceService::GetPromoServiceVersion() { | |
| 177 PrefService* local_state = g_browser_process->local_state(); | |
| 178 return local_state->GetInteger(prefs::kNTPPromoVersion); | |
| 179 } | |
| 180 | |
| 181 std::string PromoResourceService::GetPromoLocale() { | |
| 182 PrefService* local_state = g_browser_process->local_state(); | |
| 183 return local_state->GetString(prefs::kNTPPromoLocale); | |
| 184 } | |
| 185 | |
| 112 void PromoResourceService::UnpackPromoSignal( | 186 void PromoResourceService::UnpackPromoSignal( |
| 113 const DictionaryValue& parsed_json) { | 187 const DictionaryValue& parsed_json) { |
| 114 DictionaryValue* topic_dict; | 188 DictionaryValue* topic_dict; |
| 115 ListValue* answer_list; | 189 ListValue* answer_list; |
| 116 double old_promo_start = 0; | 190 double old_promo_start = 0; |
| 117 double old_promo_end = 0; | 191 double old_promo_end = 0; |
| 118 double promo_start = 0; | 192 double promo_start = 0; |
| 119 double promo_end = 0; | 193 double promo_end = 0; |
| 120 | 194 |
| 121 // Check for preexisting start and end values. | 195 // Check for preexisting start and end values. |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 201 // Also reset the promo closed preference, to signal a new promo. | 275 // Also reset the promo closed preference, to signal a new promo. |
| 202 if (!(old_promo_start == promo_start) || | 276 if (!(old_promo_start == promo_start) || |
| 203 !(old_promo_end == promo_end)) { | 277 !(old_promo_end == promo_end)) { |
| 204 prefs_->SetDouble(prefs::kNTPPromoStart, promo_start); | 278 prefs_->SetDouble(prefs::kNTPPromoStart, promo_start); |
| 205 prefs_->SetDouble(prefs::kNTPPromoEnd, promo_end); | 279 prefs_->SetDouble(prefs::kNTPPromoEnd, promo_end); |
| 206 prefs_->SetBoolean(prefs::kNTPPromoClosed, false); | 280 prefs_->SetBoolean(prefs::kNTPPromoClosed, false); |
| 207 ScheduleNotification(promo_start, promo_end); | 281 ScheduleNotification(promo_start, promo_end); |
| 208 } | 282 } |
| 209 } | 283 } |
| 210 | 284 |
| 285 void PromoResourceService::UnpackWebStoreSignal( | |
| 286 const DictionaryValue& parsed_json) { | |
| 287 DictionaryValue* topic_dict; | |
| 288 ListValue* answer_list; | |
| 289 | |
| 290 bool signal_found = false; | |
| 291 std::string promo_id = ""; | |
| 292 std::string promo_header = ""; | |
| 293 std::string promo_button = ""; | |
| 294 std::string promo_link = ""; | |
| 295 std::string promo_expire = ""; | |
| 296 std::string promo_builds = ""; | |
| 297 int target_builds; | |
| 298 | |
| 299 if (!parsed_json.GetDictionary("topic", &topic_dict) || | |
| 300 !topic_dict->GetList("answers", &answer_list)) | |
| 301 return; | |
| 302 | |
| 303 for (ListValue::const_iterator tip_iter = answer_list->begin(); | |
|
Miranda Callahan
2011/04/13 14:29:29
can you change the name from the atavistic "tip_it
jstritar
2011/04/13 19:52:27
Done. I changed them to "answer_iter". Another opt
| |
| 304 tip_iter != answer_list->end(); ++tip_iter) { | |
| 305 if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY)) | |
| 306 continue; | |
| 307 DictionaryValue* a_dic = | |
| 308 static_cast<DictionaryValue*>(*tip_iter); | |
| 309 std::string promo_signal; | |
| 310 if (!a_dic->GetString("name", &promo_signal) || | |
| 311 promo_signal != "webstore_promo") | |
| 312 continue; | |
| 313 | |
| 314 if (!a_dic->GetString(kAnswerIdProperty, &promo_id) || | |
| 315 !a_dic->GetString(kWebStoreHeaderProperty, &promo_header) || | |
| 316 !a_dic->GetString(kWebStoreButtonProperty, &promo_button) || | |
| 317 !a_dic->GetString(kWebStoreLinkProperty, &promo_link) || | |
| 318 !a_dic->GetString(kWebStoreExpireProperty, &promo_expire) || | |
| 319 !a_dic->GetString(kWebStoreBuildsProperty, &promo_builds)) | |
| 320 continue; | |
| 321 | |
| 322 if (!base::StringToInt(promo_builds, &target_builds)) | |
| 323 continue; | |
| 324 | |
| 325 if (IsThisBuildTargeted(target_builds)) { | |
| 326 // Store the first web store promo that targets the current build. | |
| 327 AppsPromo::SetPromo( | |
| 328 promo_id, promo_header, promo_button, GURL(promo_link), promo_expire); | |
| 329 signal_found = true; | |
| 330 break; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 if (!signal_found) { | |
| 335 // If no web store promos target this build, then clear all the prefs. | |
| 336 AppsPromo::ClearPromo(); | |
| 337 } | |
| 338 | |
| 339 NotificationService::current()->Notify( | |
| 340 NotificationType::WEB_STORE_PROMO_LOADED, | |
| 341 Source<PromoResourceService>(this), | |
| 342 NotificationService::NoDetails()); | |
| 343 | |
| 344 return; | |
| 345 } | |
| 346 | |
| 211 void PromoResourceService::UnpackLogoSignal( | 347 void PromoResourceService::UnpackLogoSignal( |
| 212 const DictionaryValue& parsed_json) { | 348 const DictionaryValue& parsed_json) { |
| 213 DictionaryValue* topic_dict; | 349 DictionaryValue* topic_dict; |
| 214 ListValue* answer_list; | 350 ListValue* answer_list; |
| 215 double old_logo_start = 0; | 351 double old_logo_start = 0; |
| 216 double old_logo_end = 0; | 352 double old_logo_end = 0; |
| 217 double logo_start = 0; | 353 double logo_start = 0; |
| 218 double logo_end = 0; | 354 double logo_end = 0; |
| 219 | 355 |
| 220 // Check for preexisting start and end values. | 356 // Check for preexisting start and end values. |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 PrefService* prefs = profile->GetPrefs(); | 419 PrefService* prefs = profile->GetPrefs(); |
| 284 if (prefs->HasPrefPath(prefs::kNTPPromoClosed)) | 420 if (prefs->HasPrefPath(prefs::kNTPPromoClosed)) |
| 285 promo_closed = prefs->GetBoolean(prefs::kNTPPromoClosed); | 421 promo_closed = prefs->GetBoolean(prefs::kNTPPromoClosed); |
| 286 | 422 |
| 287 // Only show if not synced. | 423 // Only show if not synced. |
| 288 bool is_synced = | 424 bool is_synced = |
| 289 (profile->HasProfileSyncService() && | 425 (profile->HasProfileSyncService() && |
| 290 sync_ui_util::GetStatus( | 426 sync_ui_util::GetStatus( |
| 291 profile->GetProfileSyncService()) == sync_ui_util::SYNCED); | 427 profile->GetProfileSyncService()) == sync_ui_util::SYNCED); |
| 292 | 428 |
| 293 // GetVersionStringModifier hits the registry. See http://crbug.com/70898. | |
| 294 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 295 const std::string channel = platform_util::GetVersionStringModifier(); | |
| 296 bool is_promo_build = false; | 429 bool is_promo_build = false; |
| 297 if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) { | 430 if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) { |
| 298 int builds_allowed = prefs->GetInteger(prefs::kNTPPromoBuild); | 431 // GetVersionStringModifier hits the registry. See http://crbug.com/70898. |
| 299 if (builds_allowed == NO_BUILD) | 432 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 300 return false; | 433 const std::string channel = platform_util::GetVersionStringModifier(); |
| 301 if (channel == "dev" || channel == "dev-m") { | 434 is_promo_build = PromoResourceService::IsBuildTargeted( |
| 302 is_promo_build = (DEV_BUILD & builds_allowed) != 0; | 435 channel, prefs->GetInteger(prefs::kNTPPromoBuild)); |
| 303 } else if (channel == "beta" || channel == "beta-m") { | |
| 304 is_promo_build = (BETA_BUILD & builds_allowed) != 0; | |
| 305 } else if (channel == "" || channel == "m") { | |
| 306 is_promo_build = (STABLE_BUILD & builds_allowed) != 0; | |
| 307 } else { | |
| 308 is_promo_build = false; | |
| 309 } | |
| 310 } | 436 } |
| 311 | 437 |
| 312 return !promo_closed && !is_synced && is_promo_build; | 438 return !promo_closed && !is_synced && is_promo_build; |
| 313 } | 439 } |
| 314 | 440 |
| 315 } // namespace PromoResourceServiceUtil | 441 } // namespace PromoResourceServiceUtil |
| 316 | |
| OLD | NEW |