blob: 6fcb28fd0636da132752bea6bcb4a4d62e8be445 [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 "components/web_resource/promo_resource_service.h"
#include <stddef.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/web_resource/notification_promo.h"
#include "components/web_resource/web_resource_pref_names.h"
#include "components/web_resource/web_resource_switches.h"
#include "url/gurl.h"
namespace web_resource {
namespace {
// Delay on first fetch so we don't interfere with startup.
const int kStartResourceFetchDelay = 5000;
// Delay between calls to fetch the promo json: 6 hours in production, and 3 min
// in debug.
const int kCacheUpdateDelay = 6 * 60 * 60 * 1000;
const int kTestCacheUpdateDelay = 3 * 60 * 1000;
// The promotion type used for Unpack() and ScheduleNotificationOnInit().
const NotificationPromo::PromoType kValidPromoTypes[] = {
#if defined(OS_ANDROID) || defined(OS_IOS)
NotificationPromo::MOBILE_NTP_SYNC_PROMO,
#if defined(OS_IOS)
NotificationPromo::MOBILE_NTP_WHATS_NEW_PROMO,
#endif // defined(OS_IOS)
#else
NotificationPromo::NTP_NOTIFICATION_PROMO,
NotificationPromo::NTP_BUBBLE_PROMO,
#endif
};
GURL GetPromoResourceURL(version_info::Channel channel) {
const std::string promo_server_url =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kPromoServerURL);
return promo_server_url.empty() ? NotificationPromo::PromoServerURL(channel)
: GURL(promo_server_url);
}
bool IsTest() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kPromoServerURL);
}
int GetCacheUpdateDelay() {
return IsTest() ? kTestCacheUpdateDelay : kCacheUpdateDelay;
}
} // namespace
// static
void PromoResourceService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, "0");
NotificationPromo::RegisterPrefs(registry);
}
// static
void PromoResourceService::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
// TODO(dbeam): This is registered only for migration; remove in M28
// when all prefs have been cleared. http://crbug.com/168887
registry->RegisterStringPref(prefs::kNtpPromoResourceCacheUpdate, "0");
NotificationPromo::RegisterProfilePrefs(registry);
}
// static
void PromoResourceService::MigrateUserPrefs(PrefService* user_prefs) {
user_prefs->ClearPref(prefs::kNtpPromoResourceCacheUpdate);
NotificationPromo::MigrateUserPrefs(user_prefs);
}
PromoResourceService::PromoResourceService(
PrefService* local_state,
version_info::Channel channel,
const std::string& application_locale,
net::URLRequestContextGetter* request_context,
const char* disable_network_switch,
const ParseJSONCallback& parse_json_callback)
: WebResourceService(local_state,
GetPromoResourceURL(channel),
application_locale, // append locale to URL
prefs::kNtpPromoResourceCacheUpdate,
kStartResourceFetchDelay,
GetCacheUpdateDelay(),
request_context,
disable_network_switch,
parse_json_callback),
weak_ptr_factory_(this) {
ScheduleNotificationOnInit();
}
PromoResourceService::~PromoResourceService() {}
std::unique_ptr<PromoResourceService::StateChangedSubscription>
PromoResourceService::RegisterStateChangedCallback(
const base::Closure& closure) {
return callback_list_.Add(closure);
}
void PromoResourceService::ScheduleNotification(
const NotificationPromo& notification_promo) {
const double promo_start = notification_promo.StartTimeForGroup();
const double promo_end = notification_promo.EndTime();
if (promo_start > 0 && promo_end > 0) {
const int64_t ms_until_start = static_cast<int64_t>(
(base::Time::FromDoubleT(promo_start) - base::Time::Now())
.InMilliseconds());
const int64_t ms_until_end = static_cast<int64_t>(
(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() {
// If the promo start is in the future, set a notification task to
// invalidate the NTP cache at the time of the promo start.
for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) {
NotificationPromo notification_promo(prefs_);
notification_promo.InitFromPrefs(kValidPromoTypes[i]);
ScheduleNotification(notification_promo);
}
}
void PromoResourceService::PostNotification(int64_t 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) {
base::ThreadTaskRunnerHandle::Get()->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() {
callback_list_.Notify();
}
void PromoResourceService::Unpack(const base::DictionaryValue& parsed_json) {
for (size_t i = 0; i < arraysize(kValidPromoTypes); ++i) {
NotificationPromo notification_promo(prefs_);
notification_promo.InitFromJson(parsed_json, kValidPromoTypes[i]);
if (notification_promo.new_notification())
ScheduleNotification(notification_promo);
}
}
} // namespace web_resource