Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(368)

Side by Side Diff: chrome/browser/web_resource/promo_resource_service.cc

Issue 6542003: Refactor WebResourceService class, making it more generic. Move all the prom... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: For the records Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/string_number_conversions.h"
8 #include "base/threading/thread_restrictions.h"
9 #include "base/time.h"
10 #include "base/values.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/browser_thread.h"
13 #include "chrome/browser/platform_util.h"
14 #include "chrome/browser/prefs/pref_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sync/sync_ui_util.h"
17 #include "chrome/common/notification_service.h"
18 #include "chrome/common/notification_type.h"
19 #include "chrome/common/pref_names.h"
20
21 namespace {
22
23 // Delay on first fetch so we don't interfere with startup.
24 static const int kStartResourceFetchDelay = 5000;
25
26 // Delay between calls to update the cache (48 hours).
27 static const int kCacheUpdateDelay = 48 * 60 * 60 * 1000;
28
29 // Users are randomly assigned to one of kNTPPromoGroupSize buckets, in order
30 // to be able to roll out promos slowly, or display different promos to
31 // different groups.
32 static const int kNTPPromoGroupSize = 16;
33
34 // Maximum number of hours for each time slice (4 weeks).
35 static const int kMaxTimeSliceHours = 24 * 7 * 4;
36
37 // Used to determine which build type should be shown a given promo.
38 enum BuildType {
39 DEV_BUILD = 1,
40 BETA_BUILD = 1 << 1,
41 STABLE_BUILD = 1 << 2,
42 };
43
44 } // namespace
45
46 const char* PromoResourceService::kCurrentTipPrefName = "current_tip";
47 const char* PromoResourceService::kTipCachePrefName = "tips";
48
49 // Server for dynamically loaded NTP HTML elements. TODO(mirandac): append
50 // locale for future usage, when we're serving localizable strings.
51 const char* PromoResourceService::kDefaultPromoResourceServer =
52 "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl=";
53
54 PromoResourceService::PromoResourceService(Profile* profile)
55 : WebResourceService(profile,
56 PromoResourceService::kDefaultPromoResourceServer,
57 true, // append locale to URL
58 NotificationType::PROMO_RESOURCE_STATE_CHANGED,
59 prefs::kNTPPromoResourceCacheUpdate,
60 kStartResourceFetchDelay,
61 kCacheUpdateDelay),
62 web_resource_cache_(NULL) {
63 Init();
64 }
65
66 PromoResourceService::~PromoResourceService() { }
67
68 void PromoResourceService::Init() {
69 prefs_->RegisterStringPref(prefs::kNTPPromoResourceCacheUpdate, "0");
70 prefs_->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0);
71 prefs_->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0);
72 prefs_->RegisterDoublePref(prefs::kNTPPromoStart, 0);
73 prefs_->RegisterDoublePref(prefs::kNTPPromoEnd, 0);
74 prefs_->RegisterStringPref(prefs::kNTPPromoLine, std::string());
75 prefs_->RegisterBooleanPref(prefs::kNTPPromoClosed, false);
76 prefs_->RegisterIntegerPref(prefs::kNTPPromoGroup, -1);
77 prefs_->RegisterIntegerPref(prefs::kNTPPromoBuild,
78 DEV_BUILD | BETA_BUILD | STABLE_BUILD);
79 prefs_->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0);
80
81 // If the promo start is in the future, set a notification task to invalidate
82 // the NTP cache at the time of the promo start.
83 double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
84 double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
85 ScheduleNotification(promo_start, promo_end);
86 }
87
88 void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
89 UnpackLogoSignal(parsed_json);
90 UnpackPromoSignal(parsed_json);
91 }
92
93 void PromoResourceService::ScheduleNotification(double promo_start,
94 double promo_end) {
95 if (promo_start > 0 && promo_end > 0) {
96 int64 ms_until_start =
97 static_cast<int64>((base::Time::FromDoubleT(
98 promo_start) - base::Time::Now()).InMilliseconds());
99 int64 ms_until_end =
100 static_cast<int64>((base::Time::FromDoubleT(
101 promo_end) - base::Time::Now()).InMilliseconds());
102 if (ms_until_start > 0)
103 PostNotification(ms_until_start);
104 if (ms_until_end > 0) {
105 PostNotification(ms_until_end);
106 if (ms_until_start <= 0) {
107 // Notify immediately if time is between start and end.
108 PostNotification(0);
109 }
110 }
111 }
112 }
113
114 void PromoResourceService::UnpackTips(const DictionaryValue& parsed_json) {
115 // Get dictionary of cached preferences.
116 web_resource_cache_ =
117 prefs_->GetMutableDictionary(prefs::kNTPPromoResourceCache);
118
119 // The list of individual tips.
120 ListValue* tip_holder = new ListValue();
121 web_resource_cache_->Set(PromoResourceService::kTipCachePrefName, tip_holder);
122
123 DictionaryValue* topic_dict;
124 ListValue* answer_list;
125 std::string topic_id;
126 std::string answer_id;
127 std::string inproduct;
128 int tip_counter = 0;
129
130 if (parsed_json.GetDictionary("topic", &topic_dict)) {
131 if (topic_dict->GetString("topic_id", &topic_id))
132 web_resource_cache_->SetString("topic_id", topic_id);
133 if (topic_dict->GetList("answers", &answer_list)) {
134 for (ListValue::const_iterator tip_iter = answer_list->begin();
135 tip_iter != answer_list->end(); ++tip_iter) {
136 if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
137 continue;
138 DictionaryValue* a_dic =
139 static_cast<DictionaryValue*>(*tip_iter);
140 if (a_dic->GetString("inproduct", &inproduct)) {
141 tip_holder->Append(Value::CreateStringValue(inproduct));
142 }
143 tip_counter++;
144 }
145 // If tips exist, set current index to 0.
146 if (!inproduct.empty()) {
147 web_resource_cache_->SetInteger(
148 PromoResourceService::kCurrentTipPrefName, 0);
149 }
150 }
151 }
152 }
153
154 void PromoResourceService::UnpackPromoSignal(
155 const DictionaryValue& parsed_json) {
156 DictionaryValue* topic_dict;
157 ListValue* answer_list;
158 double old_promo_start = 0;
159 double old_promo_end = 0;
160 double promo_start = 0;
161 double promo_end = 0;
162
163 // Check for preexisting start and end values.
164 if (prefs_->HasPrefPath(prefs::kNTPPromoStart) &&
165 prefs_->HasPrefPath(prefs::kNTPPromoEnd)) {
166 old_promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
167 old_promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
168 }
169
170 // Check for newly received start and end values.
171 if (parsed_json.GetDictionary("topic", &topic_dict)) {
172 if (topic_dict->GetList("answers", &answer_list)) {
173 std::string promo_start_string = "";
174 std::string promo_end_string = "";
175 std::string promo_string = "";
176 std::string promo_build = "";
177 int promo_build_type = 0;
178 int time_slice_hrs = 0;
179 for (ListValue::const_iterator tip_iter = answer_list->begin();
180 tip_iter != answer_list->end(); ++tip_iter) {
181 if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
182 continue;
183 DictionaryValue* a_dic =
184 static_cast<DictionaryValue*>(*tip_iter);
185 std::string promo_signal;
186 if (a_dic->GetString("name", &promo_signal)) {
187 if (promo_signal == "promo_start") {
188 a_dic->GetString("question", &promo_build);
189 size_t split = promo_build.find(":");
190 if (split != std::string::npos &&
191 base::StringToInt(promo_build.substr(0, split),
192 &promo_build_type) &&
193 base::StringToInt(promo_build.substr(split+1),
194 &time_slice_hrs) &&
195 promo_build_type >= 0 &&
196 promo_build_type <= (DEV_BUILD | BETA_BUILD | STABLE_BUILD) &&
197 time_slice_hrs >= 0 &&
198 time_slice_hrs <= kMaxTimeSliceHours) {
199 prefs_->SetInteger(prefs::kNTPPromoBuild, promo_build_type);
200 prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice,
201 time_slice_hrs);
202 } else {
203 // If no time data or bad time data are set, show promo on all
204 // builds with no time slicing.
205 prefs_->SetInteger(prefs::kNTPPromoBuild,
206 DEV_BUILD | BETA_BUILD | STABLE_BUILD);
207 prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, 0);
208 }
209 a_dic->GetString("inproduct", &promo_start_string);
210 a_dic->GetString("tooltip", &promo_string);
211 prefs_->SetString(prefs::kNTPPromoLine, promo_string);
212 srand(static_cast<uint32>(time(NULL)));
213 prefs_->SetInteger(prefs::kNTPPromoGroup,
214 rand() % kNTPPromoGroupSize);
215 } else if (promo_signal == "promo_end") {
216 a_dic->GetString("inproduct", &promo_end_string);
217 }
218 }
219 }
220 if (!promo_start_string.empty() &&
221 promo_start_string.length() > 0 &&
222 !promo_end_string.empty() &&
223 promo_end_string.length() > 0) {
224 base::Time start_time;
225 base::Time end_time;
226 if (base::Time::FromString(
227 ASCIIToWide(promo_start_string).c_str(), &start_time) &&
228 base::Time::FromString(
229 ASCIIToWide(promo_end_string).c_str(), &end_time)) {
230 // Add group time slice, adjusted from hours to seconds.
231 promo_start = start_time.ToDoubleT() +
232 (prefs_->FindPreference(prefs::kNTPPromoGroup) ?
233 prefs_->GetInteger(prefs::kNTPPromoGroup) *
234 time_slice_hrs * 60 * 60 : 0);
235 promo_end = end_time.ToDoubleT();
236 }
237 }
238 }
239 }
240
241 // If start or end times have changed, trigger a new web resource
242 // notification, so that the logo on the NTP is updated. This check is
243 // outside the reading of the web resource data, because the absence of
244 // dates counts as a triggering change if there were dates before.
245 // Also reset the promo closed preference, to signal a new promo.
246 if (!(old_promo_start == promo_start) ||
247 !(old_promo_end == promo_end)) {
248 prefs_->SetDouble(prefs::kNTPPromoStart, promo_start);
249 prefs_->SetDouble(prefs::kNTPPromoEnd, promo_end);
250 prefs_->SetBoolean(prefs::kNTPPromoClosed, false);
251 ScheduleNotification(promo_start, promo_end);
252 }
253 }
254
255 void PromoResourceService::UnpackLogoSignal(
256 const DictionaryValue& parsed_json) {
257 DictionaryValue* topic_dict;
258 ListValue* answer_list;
259 double old_logo_start = 0;
260 double old_logo_end = 0;
261 double logo_start = 0;
262 double logo_end = 0;
263
264 // Check for preexisting start and end values.
265 if (prefs_->HasPrefPath(prefs::kNTPCustomLogoStart) &&
266 prefs_->HasPrefPath(prefs::kNTPCustomLogoEnd)) {
267 old_logo_start = prefs_->GetDouble(prefs::kNTPCustomLogoStart);
268 old_logo_end = prefs_->GetDouble(prefs::kNTPCustomLogoEnd);
269 }
270
271 // Check for newly received start and end values.
272 if (parsed_json.GetDictionary("topic", &topic_dict)) {
273 if (topic_dict->GetList("answers", &answer_list)) {
274 std::string logo_start_string = "";
275 std::string logo_end_string = "";
276 for (ListValue::const_iterator tip_iter = answer_list->begin();
277 tip_iter != answer_list->end(); ++tip_iter) {
278 if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
279 continue;
280 DictionaryValue* a_dic =
281 static_cast<DictionaryValue*>(*tip_iter);
282 std::string logo_signal;
283 if (a_dic->GetString("name", &logo_signal)) {
284 if (logo_signal == "custom_logo_start") {
285 a_dic->GetString("inproduct", &logo_start_string);
286 } else if (logo_signal == "custom_logo_end") {
287 a_dic->GetString("inproduct", &logo_end_string);
288 }
289 }
290 }
291 if (!logo_start_string.empty() &&
292 logo_start_string.length() > 0 &&
293 !logo_end_string.empty() &&
294 logo_end_string.length() > 0) {
295 base::Time start_time;
296 base::Time end_time;
297 if (base::Time::FromString(
298 ASCIIToWide(logo_start_string).c_str(), &start_time) &&
299 base::Time::FromString(
300 ASCIIToWide(logo_end_string).c_str(), &end_time)) {
301 logo_start = start_time.ToDoubleT();
302 logo_end = end_time.ToDoubleT();
303 }
304 }
305 }
306 }
307
308 // If logo start or end times have changed, trigger a new web resource
309 // notification, so that the logo on the NTP is updated. This check is
310 // outside the reading of the web resource data, because the absence of
311 // dates counts as a triggering change if there were dates before.
312 if (!(old_logo_start == logo_start) ||
313 !(old_logo_end == logo_end)) {
314 prefs_->SetDouble(prefs::kNTPCustomLogoStart, logo_start);
315 prefs_->SetDouble(prefs::kNTPCustomLogoEnd, logo_end);
316 NotificationService* service = NotificationService::current();
317 service->Notify(NotificationType::PROMO_RESOURCE_STATE_CHANGED,
318 Source<WebResourceService>(this),
319 NotificationService::NoDetails());
320 }
321 }
322
323 namespace PromoResourceServiceUtil {
324
325 bool CanShowPromo(Profile* profile) {
326 bool promo_closed = false;
327 PrefService* prefs = profile->GetPrefs();
328 if (prefs->HasPrefPath(prefs::kNTPPromoClosed))
329 promo_closed = prefs->GetBoolean(prefs::kNTPPromoClosed);
330
331 // Only show if not synced.
332 bool is_synced =
333 (profile->HasProfileSyncService() &&
334 sync_ui_util::GetStatus(
335 profile->GetProfileSyncService()) == sync_ui_util::SYNCED);
336
337 // GetVersionStringModifier hits the registry. See http://crbug.com/70898.
338 base::ThreadRestrictions::ScopedAllowIO allow_io;
339 const std::string channel = platform_util::GetVersionStringModifier();
340 bool is_promo_build = false;
341 if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) {
342 int builds_allowed = prefs->GetInteger(prefs::kNTPPromoBuild);
343 if (channel == "dev") {
344 is_promo_build = (DEV_BUILD & builds_allowed) != 0;
345 } else if (channel == "beta") {
346 is_promo_build = (BETA_BUILD & builds_allowed) != 0;
347 } else if (channel == "stable") {
348 is_promo_build = (STABLE_BUILD & builds_allowed) != 0;
349 } else {
350 is_promo_build = true;
351 }
352 }
353
354 return !promo_closed && !is_synced && is_promo_build;
355 }
356
357 } // namespace PromoResourceServiceUtil
358
OLDNEW
« no previous file with comments | « chrome/browser/web_resource/promo_resource_service.h ('k') | chrome/browser/web_resource/promo_resource_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698