OLD | NEW |
(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_notification.h" |
| 6 |
| 7 #include "base/rand_util.h" |
| 8 #include "base/string_number_conversions.h" |
| 9 #include "base/time.h" |
| 10 #include "base/values.h" |
| 11 #include "chrome/browser/prefs/pref_service.h" |
| 12 #include "chrome/browser/web_resource/promo_resource_service.h" |
| 13 #include "chrome/common/pref_names.h" |
| 14 |
| 15 namespace { |
| 16 |
| 17 bool OutOfBounds(int var, int min, int max) { |
| 18 return var < min || var > max; |
| 19 } |
| 20 |
| 21 static const char kHeaderProperty[] = "topic"; |
| 22 static const char kArrayProperty[] = "answers"; |
| 23 static const char kIdentifierProperty[] = "name"; |
| 24 static const char kStartPropertyValue[] = "promo_start"; |
| 25 static const char kEndPropertyValue[] = "promo_end"; |
| 26 static const char kTextProperty[] = "tooltip"; |
| 27 static const char kTimeProperty[] = "inproduct"; |
| 28 static const char kParamsProperty[] = "question"; |
| 29 |
| 30 } // namespace |
| 31 |
| 32 PromoNotification::PromoNotification(PrefService* prefs, Delegate* delegate) |
| 33 : prefs_(prefs), |
| 34 delegate_(delegate), |
| 35 start_(0.0), |
| 36 end_(0.0), |
| 37 build_(0), |
| 38 time_slice_(0), |
| 39 max_group_(0), |
| 40 max_views_(0), |
| 41 group_(0), |
| 42 views_(0), |
| 43 text_(), |
| 44 closed_(false) { |
| 45 DCHECK(prefs); |
| 46 } |
| 47 |
| 48 void PromoNotification::InitFromJson(const DictionaryValue& json) { |
| 49 DictionaryValue* dict; |
| 50 if (json.GetDictionary(kHeaderProperty, &dict)) { |
| 51 ListValue* answers; |
| 52 if (dict->GetList(kArrayProperty, &answers)) { |
| 53 for (ListValue::const_iterator it = answers->begin(); |
| 54 it != answers->end(); |
| 55 ++it) { |
| 56 if ((*it)->IsType(Value::TYPE_DICTIONARY)) |
| 57 Parse(static_cast<DictionaryValue*>(*it)); |
| 58 } |
| 59 } |
| 60 } |
| 61 |
| 62 CheckForNewNotification(); |
| 63 } |
| 64 |
| 65 void PromoNotification::Parse(const DictionaryValue* dict) { |
| 66 std::string key; |
| 67 if (dict->GetString(kIdentifierProperty, &key)) { |
| 68 if (key == kStartPropertyValue) { |
| 69 ParseParams(dict); |
| 70 dict->GetString(kTextProperty, &text_); |
| 71 start_ = GetTimeFromDict(dict); |
| 72 } else if (key == kEndPropertyValue) { |
| 73 end_ = GetTimeFromDict(dict); |
| 74 } |
| 75 } |
| 76 } |
| 77 |
| 78 void PromoNotification::ParseParams(const DictionaryValue* dict) { |
| 79 std::string question; |
| 80 if (!dict->GetString(kParamsProperty, &question)) |
| 81 return; |
| 82 |
| 83 size_t index = 0; |
| 84 bool err = false; |
| 85 |
| 86 build_ = GetNextQuestionValue(question, &index, &err); |
| 87 time_slice_ = GetNextQuestionValue(question, &index, &err); |
| 88 max_group_ = GetNextQuestionValue(question, &index, &err); |
| 89 max_views_ = GetNextQuestionValue(question, &index, &err); |
| 90 |
| 91 if (err || |
| 92 OutOfBounds(build_, PromoResourceService::NO_BUILD, |
| 93 PromoResourceService::ALL_BUILDS) || |
| 94 OutOfBounds(time_slice_, 0, kMaxTimeSliceHours) || |
| 95 OutOfBounds(max_group_, 0, kMaxGroupSize) || |
| 96 OutOfBounds(max_views_, 0, kMaxViews)) { |
| 97 // If values are not valid, do not show promo notification. |
| 98 NOTREACHED() << "Invalid server data, question=" << question << |
| 99 ", build=" << build_ << |
| 100 ", time_slice=" << time_slice_ << |
| 101 ", max_group=" << max_group_ << |
| 102 ", max_views=" << max_views_; |
| 103 build_ = PromoResourceService::NO_BUILD; |
| 104 time_slice_ = 0; |
| 105 max_group_ = 0; |
| 106 max_views_ = 0; |
| 107 } |
| 108 } |
| 109 |
| 110 void PromoNotification::CheckForNewNotification() { |
| 111 const double old_start = GetTimeFromPrefs(prefs::kNTPPromoStart); |
| 112 const double old_end = GetTimeFromPrefs(prefs::kNTPPromoEnd); |
| 113 const bool has_views = prefs_->HasPrefPath(prefs::kNTPPromoViewsMax); |
| 114 |
| 115 // Trigger a new notification if the times have changed, or if |
| 116 // we previously never wrote out a max_views preference. |
| 117 if (old_start != start_ || old_end != end_ || !has_views) |
| 118 OnNewNotification(); |
| 119 } |
| 120 |
| 121 void PromoNotification::OnNewNotification() { |
| 122 group_ = NewGroup(); |
| 123 WritePrefs(); |
| 124 if (delegate_) |
| 125 delegate_->OnNewNotification(StartTimeWithOffset(), end_); |
| 126 } |
| 127 |
| 128 // static |
| 129 int PromoNotification::NewGroup() { |
| 130 return base::RandInt(0, kMaxGroupSize); |
| 131 } |
| 132 |
| 133 // static |
| 134 void PromoNotification::RegisterUserPrefs(PrefService* prefs) { |
| 135 prefs->RegisterDoublePref(prefs::kNTPPromoStart, |
| 136 0, |
| 137 PrefService::UNSYNCABLE_PREF); |
| 138 prefs->RegisterDoublePref(prefs::kNTPPromoEnd, |
| 139 0, |
| 140 PrefService::UNSYNCABLE_PREF); |
| 141 |
| 142 prefs->RegisterIntegerPref(prefs::kNTPPromoBuild, |
| 143 PromoResourceService::ALL_BUILDS, |
| 144 PrefService::UNSYNCABLE_PREF); |
| 145 prefs->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, |
| 146 0, |
| 147 PrefService::UNSYNCABLE_PREF); |
| 148 prefs->RegisterIntegerPref(prefs::kNTPPromoGroupMax, |
| 149 0, |
| 150 PrefService::UNSYNCABLE_PREF); |
| 151 prefs->RegisterIntegerPref(prefs::kNTPPromoViewsMax, |
| 152 0, |
| 153 PrefService::UNSYNCABLE_PREF); |
| 154 |
| 155 prefs->RegisterStringPref(prefs::kNTPPromoLine, |
| 156 std::string(), |
| 157 PrefService::UNSYNCABLE_PREF); |
| 158 prefs->RegisterIntegerPref(prefs::kNTPPromoGroup, |
| 159 0, |
| 160 PrefService::UNSYNCABLE_PREF); |
| 161 prefs->RegisterIntegerPref(prefs::kNTPPromoViews, |
| 162 0, |
| 163 PrefService::UNSYNCABLE_PREF); |
| 164 prefs->RegisterBooleanPref(prefs::kNTPPromoClosed, |
| 165 false, |
| 166 PrefService::UNSYNCABLE_PREF); |
| 167 } |
| 168 |
| 169 |
| 170 void PromoNotification::WritePrefs() { |
| 171 prefs_->SetDouble(prefs::kNTPPromoStart, start_); |
| 172 prefs_->SetDouble(prefs::kNTPPromoEnd, end_); |
| 173 |
| 174 prefs_->SetInteger(prefs::kNTPPromoBuild, build_); |
| 175 prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, time_slice_); |
| 176 prefs_->SetInteger(prefs::kNTPPromoGroupMax, max_group_); |
| 177 prefs_->SetInteger(prefs::kNTPPromoViewsMax, max_views_); |
| 178 |
| 179 prefs_->SetString(prefs::kNTPPromoLine, text_); |
| 180 prefs_->SetInteger(prefs::kNTPPromoGroup, group_); |
| 181 prefs_->SetInteger(prefs::kNTPPromoViews, views_); |
| 182 prefs_->SetBoolean(prefs::kNTPPromoClosed, closed_); |
| 183 } |
| 184 |
| 185 void PromoNotification::InitFromPrefs() { |
| 186 if (prefs_->HasPrefPath(prefs::kNTPPromoStart)) |
| 187 start_ = prefs_->GetDouble(prefs::kNTPPromoStart); |
| 188 |
| 189 if (prefs_->HasPrefPath(prefs::kNTPPromoEnd)) |
| 190 end_ = prefs_->GetDouble(prefs::kNTPPromoEnd); |
| 191 |
| 192 if (prefs_->HasPrefPath(prefs::kNTPPromoBuild)) |
| 193 build_ = prefs_->GetInteger(prefs::kNTPPromoBuild); |
| 194 |
| 195 if (prefs_->HasPrefPath(prefs::kNTPPromoGroupTimeSlice)) |
| 196 time_slice_ = prefs_->GetInteger(prefs::kNTPPromoGroupTimeSlice); |
| 197 |
| 198 if (prefs_->HasPrefPath(prefs::kNTPPromoGroupMax)) |
| 199 max_group_ = prefs_->GetInteger(prefs::kNTPPromoGroupMax); |
| 200 |
| 201 if (prefs_->HasPrefPath(prefs::kNTPPromoViewsMax)) |
| 202 max_views_ = prefs_->GetInteger(prefs::kNTPPromoViewsMax); |
| 203 |
| 204 if (prefs_->HasPrefPath(prefs::kNTPPromoLine)) |
| 205 text_ = prefs_->GetString(prefs::kNTPPromoLine); |
| 206 |
| 207 if (prefs_->HasPrefPath(prefs::kNTPPromoGroup)) |
| 208 group_ = prefs_->GetInteger(prefs::kNTPPromoGroup); |
| 209 |
| 210 if (prefs_->HasPrefPath(prefs::kNTPPromoViews)) |
| 211 views_ = prefs_->GetInteger(prefs::kNTPPromoViews); |
| 212 |
| 213 if (prefs_->HasPrefPath(prefs::kNTPPromoClosed)) |
| 214 closed_ = prefs_->GetBoolean(prefs::kNTPPromoClosed); |
| 215 } |
| 216 |
| 217 bool PromoNotification::CanShow() const { |
| 218 return !closed_ && |
| 219 !text_.empty() && |
| 220 group_ < max_group_ && |
| 221 views_ < max_views_ && |
| 222 IsBuildAllowed(build_) && |
| 223 base::Time::FromDoubleT(StartTimeWithOffset()) < base::Time::Now() && |
| 224 base::Time::FromDoubleT(end_) > base::Time::Now(); |
| 225 } |
| 226 |
| 227 bool PromoNotification::IsBuildAllowed(int builds_allowed) const { |
| 228 if (delegate_) // For testing. |
| 229 return delegate_->IsBuildAllowed(builds_allowed); |
| 230 else |
| 231 return PromoResourceService::IsBuildTargeted( |
| 232 PromoResourceService::GetChannel(), builds_allowed); |
| 233 } |
| 234 |
| 235 double PromoNotification::StartTimeWithOffset() const { |
| 236 // Adjust start using group and time slice, adjusted from hours to seconds. |
| 237 static const double kSecondsInHour = 60.0 * 60.0; |
| 238 return start_ + group_ * time_slice_ * kSecondsInHour; |
| 239 } |
| 240 |
| 241 // static |
| 242 int PromoNotification::GetNextQuestionValue(const std::string& question, |
| 243 size_t* index, |
| 244 bool* err) { |
| 245 if (*err) |
| 246 return 0; |
| 247 |
| 248 size_t new_index = question.find(':', *index); |
| 249 // Note that substr correctly handles npos. |
| 250 std::string fragment(question.substr(*index, new_index - *index)); |
| 251 *index = new_index + 1; |
| 252 |
| 253 int value; |
| 254 *err = !base::StringToInt(fragment, &value); |
| 255 return *err ? 0 : value; |
| 256 } |
| 257 |
| 258 // static |
| 259 double PromoNotification::GetTimeFromDict(const DictionaryValue* dict) { |
| 260 std::string time_str; |
| 261 if (!dict->GetString(kTimeProperty, &time_str)) |
| 262 return 0.0; |
| 263 |
| 264 base::Time time; |
| 265 if (time_str.empty() || !base::Time::FromString(time_str.c_str(), &time)) |
| 266 return 0.0; |
| 267 |
| 268 return time.ToDoubleT(); |
| 269 } |
| 270 |
| 271 double PromoNotification::GetTimeFromPrefs(const char* pref) const { |
| 272 return prefs_->HasPrefPath(pref) ? prefs_->GetDouble(pref) : 0.0; |
| 273 } |
| 274 |
| 275 bool PromoNotification::operator==(const PromoNotification& other) const { |
| 276 return prefs_ == other.prefs_ && |
| 277 delegate_ == other.delegate_ && |
| 278 start_ == other.start_ && |
| 279 end_ == other.end_ && |
| 280 build_ == other.build_ && |
| 281 time_slice_ == other.time_slice_ && |
| 282 max_group_ == other.max_group_ && |
| 283 max_views_ == other.max_views_ && |
| 284 group_ == other.group_ && |
| 285 views_ == other.views_ && |
| 286 text_ == other.text_ && |
| 287 closed_ == other.closed_; |
| 288 } |
OLD | NEW |