blob: c4f09485dad190b54da084b1fe02707ff3a046c1 [file] [log] [blame]
// Copyright (c) 2012 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/promo_resource_service.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "googleurl/src/gurl.h"
namespace {
// Delay on first fetch so we don't interfere with startup.
static const int kStartResourceFetchDelay = 5000;
// Delay between calls to update the cache (12h), and 10s in debug mode.
static const int kCacheUpdateDelay = 12 * 60 * 60 * 1000;
static const int kTestCacheUpdateDelay = 10 * 1000;
// The version of the service (used to expire the cache when upgrading Chrome
// to versions with different types of promos).
static const int kPromoServiceVersion = 7;
GURL GetPromoResourceURL() {
const std::string promo_server_url = CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switches::kPromoServerURL);
return promo_server_url.empty() ?
NotificationPromo::PromoServerURL() : GURL(promo_server_url);
}
bool IsTest() {
return CommandLine::ForCurrentProcess()->HasSwitch(switches::kPromoServerURL);
}
int GetCacheUpdateDelay() {
return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay;
}
} // namespace
// static
void PromoResourceService::RegisterPrefs(PrefService* local_state) {
local_state->RegisterIntegerPref(prefs::kNtpPromoVersion, 0);
local_state->RegisterStringPref(prefs::kNtpPromoLocale, std::string());
}
// static
void PromoResourceService::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate,
"0",
PrefService::UNSYNCABLE_PREF);
NotificationPromo::RegisterUserPrefs(prefs);
// TODO(achuith): Delete this in M22.
prefs->RegisterDoublePref(prefs::kNtpCustomLogoStart,
0,
PrefService::UNSYNCABLE_PREF);
prefs->RegisterDoublePref(prefs::kNtpCustomLogoEnd,
0,
PrefService::UNSYNCABLE_PREF);
prefs->ClearPref(prefs::kNtpCustomLogoStart);
prefs->ClearPref(prefs::kNtpCustomLogoEnd);
}
PromoResourceService::PromoResourceService(Profile* profile)
: WebResourceService(profile->GetPrefs(),
GetPromoResourceURL(),
true, // append locale to URL
prefs::kNtpPromoResourceCacheUpdate,
kStartResourceFetchDelay,
GetCacheUpdateDelay()),
profile_(profile),
ALLOW_THIS_IN_INITIALIZER_LIST(
weak_ptr_factory_(this)) {
ScheduleNotificationOnInit();
}
PromoResourceService::~PromoResourceService() {
}
void PromoResourceService::ScheduleNotification(double promo_start,
double promo_end) {
if (promo_start > 0 && promo_end > 0) {
const int64 ms_until_start =
static_cast<int64>((base::Time::FromDoubleT(
promo_start) - base::Time::Now()).InMilliseconds());
const int64 ms_until_end =
static_cast<int64>((base::Time::FromDoubleT(
promo_end) - base::Time::Now()).InMilliseconds());
if (ms_until_start > 0) {
// Schedule the next notification to happen at the start of promotion.
PostNotification(ms_until_start);
} else if (ms_until_end > 0) {
if (ms_until_start <= 0) {
// The promo is active. Notify immediately.
PostNotification(0);
}
// Schedule the next notification to happen at the end of promotion.
PostNotification(ms_until_end);
} else {
// The promo (if any) has finished. Notify immediately.
PostNotification(0);
}
} else {
// The promo (if any) was apparently cancelled. Notify immediately.
PostNotification(0);
}
}
void PromoResourceService::ScheduleNotificationOnInit() {
std::string locale = g_browser_process->GetApplicationLocale();
if (GetPromoServiceVersion() != kPromoServiceVersion ||
GetPromoLocale() != locale) {
// If the promo service has been upgraded or Chrome switched locales,
// refresh the promos.
// TODO(achuith): Mixing local_state and prefs does not work for
// multi-profile case. We should probably store version/locale in prefs_
// as well.
PrefService* local_state = g_browser_process->local_state();
local_state->SetInteger(prefs::kNtpPromoVersion, kPromoServiceVersion);
local_state->SetString(prefs::kNtpPromoLocale, locale);
prefs_->ClearPref(prefs::kNtpPromoResourceCacheUpdate);
PostNotification(0);
} else {
// If the promo start is in the future, set a notification task to
// invalidate the NTP cache at the time of the promo start.
double promo_start = prefs_->GetDouble(prefs::kNtpPromoStart);
double promo_end = prefs_->GetDouble(prefs::kNtpPromoEnd);
ScheduleNotification(promo_start, promo_end);
}
}
void PromoResourceService::PostNotification(int64 delay_ms) {
// Note that this could cause re-issuing a notification every time
// we receive an update from a server if something goes wrong.
// Given that this couldn't happen more frequently than every
// kCacheUpdateDelay milliseconds, we should be fine.
// TODO(achuith): This crashes if we post delay_ms = 0 to the message loop.
// during startup.
if (delay_ms > 0) {
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&PromoResourceService::PromoResourceStateChange,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(delay_ms));
} else if (delay_ms == 0) {
PromoResourceStateChange();
}
}
void PromoResourceService::PromoResourceStateChange() {
content::NotificationService* service =
content::NotificationService::current();
service->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED,
content::Source<WebResourceService>(this),
content::NotificationService::NoDetails());
}
int PromoResourceService::GetPromoServiceVersion() {
PrefService* local_state = g_browser_process->local_state();
return local_state->GetInteger(prefs::kNtpPromoVersion);
}
std::string PromoResourceService::GetPromoLocale() {
PrefService* local_state = g_browser_process->local_state();
return local_state->GetString(prefs::kNtpPromoLocale);
}
void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
NotificationPromo notification_promo(profile_);
notification_promo.InitFromJson(parsed_json);
if (notification_promo.new_notification()) {
ScheduleNotification(notification_promo.StartTimeForGroup(),
notification_promo.EndTime());
}
}
bool PromoResourceService::CanShowNotificationPromo(Profile* profile) {
NotificationPromo notification_promo(profile);
notification_promo.InitFromPrefs();
return notification_promo.CanShow();
}