Home | History | Annotate | Download | only in web_resource
      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 version of the service (used to expire the cache when upgrading Chrome
     34 // to versions with different types of promos).
     35 const int kPromoServiceVersion = 7;
     36 
     37 // The promotion type used for Unpack() and ScheduleNotificationOnInit().
     38 const 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 
     47 GURL 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 
     54 bool IsTest() {
     55   return CommandLine::ForCurrentProcess()->HasSwitch(switches::kPromoServerURL);
     56 }
     57 
     58 int GetCacheUpdateDelay() {
     59   return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay;
     60 }
     61 
     62 }  // namespace
     63 
     64 // static
     65 void PromoResourceService::RegisterPrefs(PrefRegistrySimple* registry) {
     66   registry->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, "0");
     67   NotificationPromo::RegisterPrefs(registry);
     68 }
     69 
     70 // static
     71 void 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
     83 void PromoResourceService::MigrateUserPrefs(PrefService* user_prefs) {
     84   user_prefs->ClearPref(prefs::kNtpPromoResourceCacheUpdate);
     85   NotificationPromo::MigrateUserPrefs(user_prefs);
     86 }
     87 
     88 PromoResourceService::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 
     99 PromoResourceService::~PromoResourceService() {
    100 }
    101 
    102 void 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 
    134 void 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 
    144 void 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 
    162 void 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 
    170 void 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