| // 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 |