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.
5 #include "chrome/browser/web_resource/promo_resource_service.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 "components/pref_registry/pref_registry_syncable.h"
18 #include "components/web_resource/web_resource_pref_names.h"
19 #include "components/web_resource/web_resource_switches.h"
20 #include "content/public/browser/notification_service.h"
25 // Delay on first fetch so we don't interfere with startup.
26 const int kStartResourceFetchDelay
= 5000;
28 // Delay between calls to fetch the promo json: 6 hours in production, and 3 min
30 const int kCacheUpdateDelay
= 6 * 60 * 60 * 1000;
31 const int kTestCacheUpdateDelay
= 3 * 60 * 1000;
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
,
38 NotificationPromo::MOBILE_NTP_WHATS_NEW_PROMO
,
39 #endif // defined(OS_IOS)
41 NotificationPromo::NTP_NOTIFICATION_PROMO
,
42 NotificationPromo::NTP_BUBBLE_PROMO
,
46 GURL
GetPromoResourceURL() {
47 const std::string promo_server_url
=
48 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
49 switches::kPromoServerURL
);
50 return promo_server_url
.empty() ?
51 NotificationPromo::PromoServerURL() : GURL(promo_server_url
);
55 return base::CommandLine::ForCurrentProcess()->HasSwitch(
56 switches::kPromoServerURL
);
59 int GetCacheUpdateDelay() {
60 return IsTest() ? kTestCacheUpdateDelay
: kCacheUpdateDelay
;
66 void PromoResourceService::RegisterPrefs(PrefRegistrySimple
* registry
) {
67 registry
->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate
, "0");
68 NotificationPromo::RegisterPrefs(registry
);
72 void PromoResourceService::RegisterProfilePrefs(
73 user_prefs::PrefRegistrySyncable
* registry
) {
74 // TODO(dbeam): This is registered only for migration; remove in M28
75 // when all prefs have been cleared. http://crbug.com/168887
76 registry
->RegisterStringPref(
77 prefs::kNtpPromoResourceCacheUpdate
,
79 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
80 NotificationPromo::RegisterProfilePrefs(registry
);
84 void PromoResourceService::MigrateUserPrefs(PrefService
* user_prefs
) {
85 user_prefs
->ClearPref(prefs::kNtpPromoResourceCacheUpdate
);
86 NotificationPromo::MigrateUserPrefs(user_prefs
);
89 PromoResourceService::PromoResourceService()
90 : ChromeWebResourceService(g_browser_process
->local_state(),
91 GetPromoResourceURL(),
92 true, // append locale to URL
93 prefs::kNtpPromoResourceCacheUpdate
,
94 kStartResourceFetchDelay
,
95 GetCacheUpdateDelay()),
96 weak_ptr_factory_(this) {
97 ScheduleNotificationOnInit();
100 PromoResourceService::~PromoResourceService() {
103 void PromoResourceService::ScheduleNotification(
104 const NotificationPromo
& notification_promo
) {
105 const double promo_start
= notification_promo
.StartTimeForGroup();
106 const double promo_end
= notification_promo
.EndTime();
108 if (promo_start
> 0 && promo_end
> 0) {
109 const int64 ms_until_start
=
110 static_cast<int64
>((base::Time::FromDoubleT(
111 promo_start
) - base::Time::Now()).InMilliseconds());
112 const int64 ms_until_end
=
113 static_cast<int64
>((base::Time::FromDoubleT(
114 promo_end
) - base::Time::Now()).InMilliseconds());
115 if (ms_until_start
> 0) {
116 // Schedule the next notification to happen at the start of promotion.
117 PostNotification(ms_until_start
);
118 } else if (ms_until_end
> 0) {
119 if (ms_until_start
<= 0) {
120 // The promo is active. Notify immediately.
123 // Schedule the next notification to happen at the end of promotion.
124 PostNotification(ms_until_end
);
126 // The promo (if any) has finished. Notify immediately.
130 // The promo (if any) was apparently cancelled. Notify immediately.
135 void PromoResourceService::ScheduleNotificationOnInit() {
136 // If the promo start is in the future, set a notification task to
137 // invalidate the NTP cache at the time of the promo start.
138 for (size_t i
= 0; i
< arraysize(kValidPromoTypes
); ++i
) {
139 NotificationPromo notification_promo
;
140 notification_promo
.InitFromPrefs(kValidPromoTypes
[i
]);
141 ScheduleNotification(notification_promo
);
145 void PromoResourceService::PostNotification(int64 delay_ms
) {
146 // Note that this could cause re-issuing a notification every time
147 // we receive an update from a server if something goes wrong.
148 // Given that this couldn't happen more frequently than every
149 // kCacheUpdateDelay milliseconds, we should be fine.
150 // TODO(achuith): This crashes if we post delay_ms = 0 to the message loop.
153 base::MessageLoop::current()->PostDelayedTask(
155 base::Bind(&PromoResourceService::PromoResourceStateChange
,
156 weak_ptr_factory_
.GetWeakPtr()),
157 base::TimeDelta::FromMilliseconds(delay_ms
));
158 } else if (delay_ms
== 0) {
159 PromoResourceStateChange();
163 void PromoResourceService::PromoResourceStateChange() {
164 content::NotificationService
* service
=
165 content::NotificationService::current();
166 service
->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED
,
167 content::Source
<WebResourceService
>(this),
168 content::NotificationService::NoDetails());
171 void PromoResourceService::Unpack(const base::DictionaryValue
& parsed_json
) {
172 for (size_t i
= 0; i
< arraysize(kValidPromoTypes
); ++i
) {
173 NotificationPromo notification_promo
;
174 notification_promo
.InitFromJson(parsed_json
, kValidPromoTypes
[i
]);
175 if (notification_promo
.new_notification())
176 ScheduleNotification(notification_promo
);