summaryrefslogtreecommitdiffstats
path: root/chrome/browser/web_resource/notification_promo.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/web_resource/notification_promo.cc')
-rw-r--r--chrome/browser/web_resource/notification_promo.cc309
1 files changed, 309 insertions, 0 deletions
diff --git a/chrome/browser/web_resource/notification_promo.cc b/chrome/browser/web_resource/notification_promo.cc
new file mode 100644
index 0000000..3a28915
--- /dev/null
+++ b/chrome/browser/web_resource/notification_promo.cc
@@ -0,0 +1,309 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_resource/notification_promo.h"
+
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/web_resource/promo_resource_service.h"
+#include "chrome/common/pref_names.h"
+
+namespace {
+
+// Maximum number of views.
+static const int kMaxViews = 1000;
+
+// Maximum number of hours for each time slice (4 weeks).
+static const int kMaxTimeSliceHours = 24 * 7 * 4;
+
+bool OutOfBounds(int var, int min, int max) {
+ return var < min || var > max;
+}
+
+static const char kHeaderProperty[] = "topic";
+static const char kArrayProperty[] = "answers";
+static const char kIdentifierProperty[] = "name";
+static const char kStartPropertyValue[] = "promo_start";
+static const char kEndPropertyValue[] = "promo_end";
+static const char kTextProperty[] = "tooltip";
+static const char kTimeProperty[] = "inproduct";
+static const char kParamsProperty[] = "question";
+
+// Time getters.
+double GetTimeFromDict(const DictionaryValue* dict) {
+ std::string time_str;
+ if (!dict->GetString(kTimeProperty, &time_str))
+ return 0.0;
+
+ base::Time time;
+ if (time_str.empty() || !base::Time::FromString(time_str.c_str(), &time))
+ return 0.0;
+
+ return time.ToDoubleT();
+}
+
+double GetTimeFromPrefs(PrefService* prefs, const char* pref) {
+ return prefs->HasPrefPath(pref) ? prefs->GetDouble(pref) : 0.0;
+}
+
+} // namespace
+
+NotificationPromo::NotificationPromo(PrefService* prefs, Delegate* delegate)
+ : prefs_(prefs),
+ delegate_(delegate),
+ start_(0.0),
+ end_(0.0),
+ build_(0),
+ time_slice_(0),
+ max_group_(0),
+ max_views_(0),
+ group_(0),
+ views_(0),
+ text_(),
+ closed_(false) {
+ DCHECK(prefs);
+}
+
+void NotificationPromo::InitFromJson(const DictionaryValue& json) {
+ DictionaryValue* dict;
+ if (json.GetDictionary(kHeaderProperty, &dict)) {
+ ListValue* answers;
+ if (dict->GetList(kArrayProperty, &answers)) {
+ for (ListValue::const_iterator it = answers->begin();
+ it != answers->end();
+ ++it) {
+ if ((*it)->IsType(Value::TYPE_DICTIONARY))
+ Parse(static_cast<DictionaryValue*>(*it));
+ }
+ }
+ }
+
+ CheckForNewNotification();
+}
+
+void NotificationPromo::Parse(const DictionaryValue* dict) {
+ std::string key;
+ if (dict->GetString(kIdentifierProperty, &key)) {
+ if (key == kStartPropertyValue) {
+ ParseParams(dict);
+ dict->GetString(kTextProperty, &text_);
+ start_ = GetTimeFromDict(dict);
+ } else if (key == kEndPropertyValue) {
+ end_ = GetTimeFromDict(dict);
+ }
+ }
+}
+
+void NotificationPromo::ParseParams(const DictionaryValue* dict) {
+ std::string question;
+ if (!dict->GetString(kParamsProperty, &question))
+ return;
+
+ size_t index = 0;
+ bool err = false;
+
+ build_ = GetNextQuestionValue(question, &index, &err);
+ time_slice_ = GetNextQuestionValue(question, &index, &err);
+ max_group_ = GetNextQuestionValue(question, &index, &err);
+ max_views_ = GetNextQuestionValue(question, &index, &err);
+
+ if (err ||
+ OutOfBounds(build_, PromoResourceService::NO_BUILD,
+ PromoResourceService::ALL_BUILDS) ||
+ OutOfBounds(time_slice_, 0, kMaxTimeSliceHours) ||
+ OutOfBounds(max_group_, 0, kMaxGroupSize) ||
+ OutOfBounds(max_views_, 0, kMaxViews)) {
+ // If values are not valid, do not show promo notification.
+ DLOG(ERROR) << "Invalid server data, question=" << question <<
+ ", build=" << build_ <<
+ ", time_slice=" << time_slice_ <<
+ ", max_group=" << max_group_ <<
+ ", max_views=" << max_views_;
+ build_ = PromoResourceService::NO_BUILD;
+ time_slice_ = 0;
+ max_group_ = 0;
+ max_views_ = 0;
+ }
+}
+
+void NotificationPromo::CheckForNewNotification() {
+ const double old_start = GetTimeFromPrefs(prefs_, prefs::kNTPPromoStart);
+ const double old_end = GetTimeFromPrefs(prefs_, prefs::kNTPPromoEnd);
+ const bool has_views = prefs_->HasPrefPath(prefs::kNTPPromoViewsMax);
+
+ // Trigger a new notification if the times have changed, or if
+ // we previously never wrote out a max_views preference.
+ if (old_start != start_ || old_end != end_ || !has_views)
+ OnNewNotification();
+}
+
+void NotificationPromo::OnNewNotification() {
+ group_ = NewGroup();
+ WritePrefs();
+ if (delegate_)
+ delegate_->OnNewNotification(StartTimeWithOffset(), end_);
+}
+
+// static
+int NotificationPromo::NewGroup() {
+ return base::RandInt(0, kMaxGroupSize);
+}
+
+// static
+void NotificationPromo::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterDoublePref(prefs::kNTPPromoStart,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterDoublePref(prefs::kNTPPromoEnd,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+
+ prefs->RegisterIntegerPref(prefs::kNTPPromoBuild,
+ PromoResourceService::ALL_BUILDS,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPPromoGroupMax,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPPromoViewsMax,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+
+ prefs->RegisterStringPref(prefs::kNTPPromoLine,
+ std::string(),
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPPromoGroup,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPPromoViews,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterBooleanPref(prefs::kNTPPromoClosed,
+ false,
+ PrefService::UNSYNCABLE_PREF);
+}
+
+
+void NotificationPromo::WritePrefs() {
+ prefs_->SetDouble(prefs::kNTPPromoStart, start_);
+ prefs_->SetDouble(prefs::kNTPPromoEnd, end_);
+
+ prefs_->SetInteger(prefs::kNTPPromoBuild, build_);
+ prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, time_slice_);
+ prefs_->SetInteger(prefs::kNTPPromoGroupMax, max_group_);
+ prefs_->SetInteger(prefs::kNTPPromoViewsMax, max_views_);
+
+ prefs_->SetString(prefs::kNTPPromoLine, text_);
+ prefs_->SetInteger(prefs::kNTPPromoGroup, group_);
+ prefs_->SetInteger(prefs::kNTPPromoViews, views_);
+ prefs_->SetBoolean(prefs::kNTPPromoClosed, closed_);
+}
+
+void NotificationPromo::InitFromPrefs() {
+ if (prefs_->HasPrefPath(prefs::kNTPPromoStart))
+ start_ = prefs_->GetDouble(prefs::kNTPPromoStart);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoEnd))
+ end_ = prefs_->GetDouble(prefs::kNTPPromoEnd);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoBuild))
+ build_ = prefs_->GetInteger(prefs::kNTPPromoBuild);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoGroupTimeSlice))
+ time_slice_ = prefs_->GetInteger(prefs::kNTPPromoGroupTimeSlice);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoGroupMax))
+ max_group_ = prefs_->GetInteger(prefs::kNTPPromoGroupMax);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoViewsMax))
+ max_views_ = prefs_->GetInteger(prefs::kNTPPromoViewsMax);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoLine))
+ text_ = prefs_->GetString(prefs::kNTPPromoLine);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoGroup))
+ group_ = prefs_->GetInteger(prefs::kNTPPromoGroup);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoViews))
+ views_ = prefs_->GetInteger(prefs::kNTPPromoViews);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoClosed))
+ closed_ = prefs_->GetBoolean(prefs::kNTPPromoClosed);
+}
+
+bool NotificationPromo::CanShow() const {
+ return !closed_ &&
+ !text_.empty() &&
+ group_ < max_group_ &&
+ views_ < max_views_ &&
+ IsBuildAllowed(build_) &&
+ base::Time::FromDoubleT(StartTimeWithOffset()) < base::Time::Now() &&
+ base::Time::FromDoubleT(end_) > base::Time::Now();
+}
+
+void NotificationPromo::HandleClosed() {
+ prefs_->SetBoolean(prefs::kNTPPromoClosed, true);
+}
+
+bool NotificationPromo::HandleViewed() {
+ if (prefs_->HasPrefPath(prefs::kNTPPromoViewsMax))
+ max_views_ = prefs_->GetInteger(prefs::kNTPPromoViewsMax);
+
+ if (prefs_->HasPrefPath(prefs::kNTPPromoViews))
+ views_ = prefs_->GetInteger(prefs::kNTPPromoViews);
+
+ prefs_->SetInteger(prefs::kNTPPromoViews, ++views_);
+ return views_ >= max_views_;
+}
+
+bool NotificationPromo::IsBuildAllowed(int builds_allowed) const {
+ if (delegate_) // For testing.
+ return delegate_->IsBuildAllowed(builds_allowed);
+ else
+ return PromoResourceService::IsBuildTargeted(
+ PromoResourceService::GetChannel(), builds_allowed);
+}
+
+double NotificationPromo::StartTimeWithOffset() const {
+ // Adjust start using group and time slice, adjusted from hours to seconds.
+ static const double kSecondsInHour = 60.0 * 60.0;
+ return start_ + group_ * time_slice_ * kSecondsInHour;
+}
+
+// static
+int NotificationPromo::GetNextQuestionValue(const std::string& question,
+ size_t* index,
+ bool* err) {
+ if (*err)
+ return 0;
+
+ size_t new_index = question.find(':', *index);
+ // Note that substr correctly handles npos.
+ std::string fragment(question.substr(*index, new_index - *index));
+ *index = new_index + 1;
+
+ int value;
+ *err = !base::StringToInt(fragment, &value);
+ return *err ? 0 : value;
+}
+
+bool NotificationPromo::operator==(const NotificationPromo& other) const {
+ return prefs_ == other.prefs_ &&
+ delegate_ == other.delegate_ &&
+ start_ == other.start_ &&
+ end_ == other.end_ &&
+ build_ == other.build_ &&
+ time_slice_ == other.time_slice_ &&
+ max_group_ == other.max_group_ &&
+ max_views_ == other.max_views_ &&
+ group_ == other.group_ &&
+ views_ == other.views_ &&
+ text_ == other.text_ &&
+ closed_ == other.closed_;
+}