promo_resource_service.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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 3// found in the LICENSE file. 4 5#include "chrome/browser/web_resource/promo_resource_service.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/message_loop/message_loop.h" 10#include "base/prefs/pref_registry_simple.h" 11#include "base/prefs/pref_service.h" 12#include "base/threading/thread_restrictions.h" 13#include "base/values.h" 14#include "chrome/browser/browser_process.h" 15#include "chrome/browser/chrome_notification_types.h" 16#include "chrome/browser/web_resource/notification_promo.h" 17#include "chrome/common/chrome_switches.h" 18#include "chrome/common/pref_names.h" 19#include "components/user_prefs/pref_registry_syncable.h" 20#include "content/public/browser/notification_service.h" 21#include "url/gurl.h" 22 23namespace { 24 25// Delay on first fetch so we don't interfere with startup. 26const int kStartResourceFetchDelay = 5000; 27 28// Delay between calls to fetch the promo json: 6 hours in production, and 3 min 29// in debug. 30const int kCacheUpdateDelay = 6 * 60 * 60 * 1000; 31const int kTestCacheUpdateDelay = 3 * 60 * 1000; 32 33// The version of the service (used to expire the cache when upgrading Chrome 34// to versions with different types of promos). 35const int kPromoServiceVersion = 7; 36 37// The promotion type used for Unpack() and ScheduleNotificationOnInit(). 38const NotificationPromo::PromoType kValidPromoTypes[] = { 39#if defined(OS_ANDROID) || defined(OS_IOS) 40 NotificationPromo::MOBILE_NTP_SYNC_PROMO, 41#else 42 NotificationPromo::NTP_NOTIFICATION_PROMO, 43 NotificationPromo::NTP_BUBBLE_PROMO, 44#endif 45}; 46 47GURL GetPromoResourceURL() { 48 const std::string promo_server_url = CommandLine::ForCurrentProcess()-> 49 GetSwitchValueASCII(switches::kPromoServerURL); 50 return promo_server_url.empty() ? 51 NotificationPromo::PromoServerURL() : GURL(promo_server_url); 52} 53 54bool IsTest() { 55 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kPromoServerURL); 56} 57 58int GetCacheUpdateDelay() { 59 return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay; 60} 61 62} // namespace 63 64// static 65void PromoResourceService::RegisterPrefs(PrefRegistrySimple* registry) { 66 registry->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, "0"); 67 NotificationPromo::RegisterPrefs(registry); 68} 69 70// static 71void PromoResourceService::RegisterProfilePrefs( 72 user_prefs::PrefRegistrySyncable* registry) { 73 // TODO(dbeam): This is registered only for migration; remove in M28 74 // when all prefs have been cleared. http://crbug.com/168887 75 registry->RegisterStringPref( 76 prefs::kNtpPromoResourceCacheUpdate, 77 "0", 78 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 79 NotificationPromo::RegisterProfilePrefs(registry); 80} 81 82// static 83void PromoResourceService::MigrateUserPrefs(PrefService* user_prefs) { 84 user_prefs->ClearPref(prefs::kNtpPromoResourceCacheUpdate); 85 NotificationPromo::MigrateUserPrefs(user_prefs); 86} 87 88PromoResourceService::PromoResourceService() 89 : WebResourceService(g_browser_process->local_state(), 90 GetPromoResourceURL(), 91 true, // append locale to URL 92 prefs::kNtpPromoResourceCacheUpdate, 93 kStartResourceFetchDelay, 94 GetCacheUpdateDelay()), 95 weak_ptr_factory_(this) { 96 ScheduleNotificationOnInit(); 97} 98 99PromoResourceService::~PromoResourceService() { 100} 101 102void PromoResourceService::ScheduleNotification( 103 const NotificationPromo& notification_promo) { 104 const double promo_start = notification_promo.StartTimeForGroup(); 105 const double promo_end = notification_promo.EndTime(); 106 107 if (promo_start > 0 && promo_end > 0) { 108 const int64 ms_until_start = 109 static_cast<int64>((base::Time::FromDoubleT( 110 promo_start) - base::Time::Now()).InMilliseconds()); 111 const int64 ms_until_end = 112 static_cast<int64>((base::Time::FromDoubleT( 113 promo_end) - base::Time::Now()).InMilliseconds()); 114 if (ms_until_start > 0) { 115 // Schedule the next notification to happen at the start of promotion. 116 PostNotification(ms_until_start); 117 } else if (ms_until_end > 0) { 118 if (ms_until_start <= 0) { 119 // The promo is active. Notify immediately. 120 PostNotification(0); 121 } 122 // Schedule the next notification to happen at the end of promotion. 123 PostNotification(ms_until_end); 124 } else { 125 // The promo (if any) has finished. Notify immediately. 126 PostNotification(0); 127 } 128 } else { 129 // The promo (if any) was apparently cancelled. Notify immediately. 130 PostNotification(0); 131 } 132} 133 134void PromoResourceService::ScheduleNotificationOnInit() { 135 // If the promo start is in the future, set a notification task to 136 // invalidate the NTP cache at the time of the promo start. 137 for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) { 138 NotificationPromo notification_promo; 139 notification_promo.InitFromPrefs(kValidPromoTypes[i]); 140 ScheduleNotification(notification_promo); 141 } 142} 143 144void PromoResourceService::PostNotification(int64 delay_ms) { 145 // Note that this could cause re-issuing a notification every time 146 // we receive an update from a server if something goes wrong. 147 // Given that this couldn't happen more frequently than every 148 // kCacheUpdateDelay milliseconds, we should be fine. 149 // TODO(achuith): This crashes if we post delay_ms = 0 to the message loop. 150 // during startup. 151 if (delay_ms > 0) { 152 base::MessageLoop::current()->PostDelayedTask( 153 FROM_HERE, 154 base::Bind(&PromoResourceService::PromoResourceStateChange, 155 weak_ptr_factory_.GetWeakPtr()), 156 base::TimeDelta::FromMilliseconds(delay_ms)); 157 } else if (delay_ms == 0) { 158 PromoResourceStateChange(); 159 } 160} 161 162void PromoResourceService::PromoResourceStateChange() { 163 content::NotificationService* service = 164 content::NotificationService::current(); 165 service->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED, 166 content::Source<WebResourceService>(this), 167 content::NotificationService::NoDetails()); 168} 169 170void PromoResourceService::Unpack(const DictionaryValue& parsed_json) { 171 for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) { 172 NotificationPromo notification_promo; 173 notification_promo.InitFromJson(parsed_json, kValidPromoTypes[i]); 174 if (notification_promo.new_notification()) 175 ScheduleNotification(notification_promo); 176 } 177} 178