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