// 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/policy/asynchronous_policy_loader.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "content/browser/browser_thread.h"
namespace policy {
AsynchronousPolicyLoader::AsynchronousPolicyLoader(
AsynchronousPolicyProvider::Delegate* delegate,
int reload_interval_minutes)
: delegate_(delegate),
reload_task_(NULL),
reload_interval_(base::TimeDelta::FromMinutes(reload_interval_minutes)),
origin_loop_(MessageLoop::current()),
stopped_(false) {}
void AsynchronousPolicyLoader::Init() {
policy_.reset(delegate_->Load());
// Initialization can happen early when the file thread is not yet available,
// but the subclass of the loader must do some of their initialization on the
// file thread. Posting to the file thread directly before it is initialized
// will cause the task to be forgotten. Instead, post a task to the ui thread
// to delay the remainder of initialization until threading is fully
// initialized.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(
this,
&AsynchronousPolicyLoader::InitAfterFileThreadAvailable));
}
void AsynchronousPolicyLoader::Stop() {
if (!stopped_) {
stopped_ = true;
delegate_.reset();
FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
observer_list_,
OnProviderGoingAway());
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(this, &AsynchronousPolicyLoader::StopOnFileThread));
}
}
AsynchronousPolicyLoader::~AsynchronousPolicyLoader() {
}
// Manages the life cycle of a new policy map during until its life cycle is
// taken over by the policy loader.
class UpdatePolicyTask : public Task {
public:
UpdatePolicyTask(scoped_refptr<AsynchronousPolicyLoader> loader,
DictionaryValue* new_policy)
: loader_(loader),
new_policy_(new_policy) {}
virtual void Run() {
loader_->UpdatePolicy(new_policy_.release());
}
private:
scoped_refptr<AsynchronousPolicyLoader> loader_;
scoped_ptr<DictionaryValue> new_policy_;
DISALLOW_COPY_AND_ASSIGN(UpdatePolicyTask);
};
void AsynchronousPolicyLoader::Reload() {
if (delegate_.get()) {
DictionaryValue* new_policy = delegate_->Load();
PostUpdatePolicyTask(new_policy);
}
}
void AsynchronousPolicyLoader::AddObserver(
ConfigurationPolicyProvider::Observer* observer) {
observer_list_.AddObserver(observer);
}
void AsynchronousPolicyLoader::RemoveObserver(
ConfigurationPolicyProvider::Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void AsynchronousPolicyLoader::CancelReloadTask() {
if (reload_task_) {
// Only check the thread if there's still a reload task. During
// destruction of unit tests, the message loop destruction can
// call this method when the file thread is no longer around,
// but in that case reload_task_ is NULL.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
reload_task_->Cancel();
reload_task_ = NULL;
}
}
void AsynchronousPolicyLoader::ScheduleReloadTask(
const base::TimeDelta& delay) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
CancelReloadTask();
reload_task_ =
NewRunnableMethod(this, &AsynchronousPolicyLoader::ReloadFromTask);
BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
delay.InMilliseconds());
}
void AsynchronousPolicyLoader::ScheduleFallbackReloadTask() {
// As a safeguard in case that the load delegate failed to timely notice a
// change in policy, schedule a reload task that'll make us recheck after a
// reasonable interval.
ScheduleReloadTask(reload_interval_);
}
void AsynchronousPolicyLoader::ReloadFromTask() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
// Drop the reference to the reload task, since the task might be the only
// referrer that keeps us alive, so we should not Cancel() it.
reload_task_ = NULL;
Reload();
}
void AsynchronousPolicyLoader::InitOnFileThread() {
}
void AsynchronousPolicyLoader::StopOnFileThread() {
CancelReloadTask();
}
void AsynchronousPolicyLoader::PostUpdatePolicyTask(
DictionaryValue* new_policy) {
origin_loop_->PostTask(FROM_HERE, new UpdatePolicyTask(this, new_policy));
}
void AsynchronousPolicyLoader::UpdatePolicy(DictionaryValue* new_policy_raw) {
scoped_ptr<DictionaryValue> new_policy(new_policy_raw);
DCHECK(policy_.get());
if (!policy_->Equals(new_policy.get())) {
policy_.reset(new_policy.release());
FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
observer_list_,
OnUpdatePolicy());
}
}
void AsynchronousPolicyLoader::InitAfterFileThreadAvailable() {
if (!stopped_) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(this, &AsynchronousPolicyLoader::InitOnFileThread));
}
}
} // namespace policy