promo_resource_service.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.h"
10#include "base/threading/thread_restrictions.h"
11#include "base/values.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/prefs/pref_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/web_resource/notification_promo.h"
16#include "chrome/common/chrome_notification_types.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/pref_names.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/notification_service.h"
21#include "googleurl/src/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(PrefService* local_state) {
66  // TODO(achuith): Delete this in M26. http://crbug.com/143773
67  // The promo service version number, and last locale.
68  const char kNtpPromoVersion[] = "ntp.promo_version";
69  const char kNtpPromoLocale[] = "ntp.promo_locale";
70  local_state->RegisterIntegerPref(kNtpPromoVersion, 0);
71  local_state->RegisterStringPref(kNtpPromoLocale, std::string());
72  local_state->ClearPref(kNtpPromoVersion);
73  local_state->ClearPref(kNtpPromoLocale);
74}
75
76// static
77void PromoResourceService::RegisterUserPrefs(PrefService* prefs) {
78  prefs->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate,
79                            "0",
80                            PrefService::UNSYNCABLE_PREF);
81  NotificationPromo::RegisterUserPrefs(prefs);
82}
83
84PromoResourceService::PromoResourceService(Profile* profile)
85    : WebResourceService(profile->GetPrefs(),
86                         GetPromoResourceURL(),
87                         true,  // append locale to URL
88                         prefs::kNtpPromoResourceCacheUpdate,
89                         kStartResourceFetchDelay,
90                         GetCacheUpdateDelay()),
91                         profile_(profile),
92                         ALLOW_THIS_IN_INITIALIZER_LIST(
93                             weak_ptr_factory_(this)) {
94  ScheduleNotificationOnInit();
95}
96
97PromoResourceService::~PromoResourceService() {
98}
99
100void PromoResourceService::ScheduleNotification(
101    const NotificationPromo& notification_promo) {
102  const double promo_start = notification_promo.StartTimeForGroup();
103  const double promo_end = notification_promo.EndTime();
104
105  if (promo_start > 0 && promo_end > 0) {
106    const int64 ms_until_start =
107        static_cast<int64>((base::Time::FromDoubleT(
108            promo_start) - base::Time::Now()).InMilliseconds());
109    const int64 ms_until_end =
110        static_cast<int64>((base::Time::FromDoubleT(
111            promo_end) - base::Time::Now()).InMilliseconds());
112    if (ms_until_start > 0) {
113      // Schedule the next notification to happen at the start of promotion.
114      PostNotification(ms_until_start);
115    } else if (ms_until_end > 0) {
116      if (ms_until_start <= 0) {
117        // The promo is active.  Notify immediately.
118        PostNotification(0);
119      }
120      // Schedule the next notification to happen at the end of promotion.
121      PostNotification(ms_until_end);
122    } else {
123      // The promo (if any) has finished.  Notify immediately.
124      PostNotification(0);
125    }
126  } else {
127      // The promo (if any) was apparently cancelled.  Notify immediately.
128      PostNotification(0);
129  }
130}
131
132void PromoResourceService::ScheduleNotificationOnInit() {
133  // If the promo start is in the future, set a notification task to
134  // invalidate the NTP cache at the time of the promo start.
135  for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) {
136    NotificationPromo notification_promo(profile_);
137    notification_promo.InitFromPrefs(kValidPromoTypes[i]);
138    ScheduleNotification(notification_promo);
139  }
140}
141
142void PromoResourceService::PostNotification(int64 delay_ms) {
143  // Note that this could cause re-issuing a notification every time
144  // we receive an update from a server if something goes wrong.
145  // Given that this couldn't happen more frequently than every
146  // kCacheUpdateDelay milliseconds, we should be fine.
147  // TODO(achuith): This crashes if we post delay_ms = 0 to the message loop.
148  // during startup.
149  if (delay_ms > 0) {
150    MessageLoop::current()->PostDelayedTask(
151        FROM_HERE,
152        base::Bind(&PromoResourceService::PromoResourceStateChange,
153                   weak_ptr_factory_.GetWeakPtr()),
154        base::TimeDelta::FromMilliseconds(delay_ms));
155  } else if (delay_ms == 0) {
156    PromoResourceStateChange();
157  }
158}
159
160void PromoResourceService::PromoResourceStateChange() {
161  content::NotificationService* service =
162      content::NotificationService::current();
163  service->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED,
164                  content::Source<WebResourceService>(this),
165                  content::NotificationService::NoDetails());
166}
167
168void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
169  for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) {
170    NotificationPromo notification_promo(profile_);
171    notification_promo.InitFromJson(parsed_json, kValidPromoTypes[i]);
172    if (notification_promo.new_notification())
173      ScheduleNotification(notification_promo);
174  }
175}
176