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

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

Powered by Google App Engine
This is Rietveld 408576698