1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/policy/asynchronous_policy_loader.h" 6 7 #include "base/message_loop.h" 8 #include "base/task.h" 9 #include "content/browser/browser_thread.h" 10 11 namespace policy { 12 13 AsynchronousPolicyLoader::AsynchronousPolicyLoader( 14 AsynchronousPolicyProvider::Delegate* delegate, 15 int reload_interval_minutes) 16 : delegate_(delegate), 17 reload_task_(NULL), 18 reload_interval_(base::TimeDelta::FromMinutes(reload_interval_minutes)), 19 origin_loop_(MessageLoop::current()), 20 stopped_(false) {} 21 22 void AsynchronousPolicyLoader::Init() { 23 policy_.reset(delegate_->Load()); 24 // Initialization can happen early when the file thread is not yet available, 25 // but the subclass of the loader must do some of their initialization on the 26 // file thread. Posting to the file thread directly before it is initialized 27 // will cause the task to be forgotten. Instead, post a task to the ui thread 28 // to delay the remainder of initialization until threading is fully 29 // initialized. 30 BrowserThread::PostTask( 31 BrowserThread::UI, FROM_HERE, 32 NewRunnableMethod( 33 this, 34 &AsynchronousPolicyLoader::InitAfterFileThreadAvailable)); 35 } 36 37 void AsynchronousPolicyLoader::Stop() { 38 if (!stopped_) { 39 stopped_ = true; 40 delegate_.reset(); 41 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer, 42 observer_list_, 43 OnProviderGoingAway()); 44 BrowserThread::PostTask( 45 BrowserThread::FILE, FROM_HERE, 46 NewRunnableMethod(this, &AsynchronousPolicyLoader::StopOnFileThread)); 47 } 48 } 49 50 AsynchronousPolicyLoader::~AsynchronousPolicyLoader() { 51 } 52 53 // Manages the life cycle of a new policy map during until its life cycle is 54 // taken over by the policy loader. 55 class UpdatePolicyTask : public Task { 56 public: 57 UpdatePolicyTask(scoped_refptr<AsynchronousPolicyLoader> loader, 58 DictionaryValue* new_policy) 59 : loader_(loader), 60 new_policy_(new_policy) {} 61 62 virtual void Run() { 63 loader_->UpdatePolicy(new_policy_.release()); 64 } 65 66 private: 67 scoped_refptr<AsynchronousPolicyLoader> loader_; 68 scoped_ptr<DictionaryValue> new_policy_; 69 DISALLOW_COPY_AND_ASSIGN(UpdatePolicyTask); 70 }; 71 72 void AsynchronousPolicyLoader::Reload() { 73 if (delegate_.get()) { 74 DictionaryValue* new_policy = delegate_->Load(); 75 PostUpdatePolicyTask(new_policy); 76 } 77 } 78 79 void AsynchronousPolicyLoader::AddObserver( 80 ConfigurationPolicyProvider::Observer* observer) { 81 observer_list_.AddObserver(observer); 82 } 83 84 void AsynchronousPolicyLoader::RemoveObserver( 85 ConfigurationPolicyProvider::Observer* observer) { 86 observer_list_.RemoveObserver(observer); 87 } 88 89 void AsynchronousPolicyLoader::CancelReloadTask() { 90 if (reload_task_) { 91 // Only check the thread if there's still a reload task. During 92 // destruction of unit tests, the message loop destruction can 93 // call this method when the file thread is no longer around, 94 // but in that case reload_task_ is NULL. 95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 96 reload_task_->Cancel(); 97 reload_task_ = NULL; 98 } 99 } 100 101 void AsynchronousPolicyLoader::ScheduleReloadTask( 102 const base::TimeDelta& delay) { 103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 104 105 CancelReloadTask(); 106 107 reload_task_ = 108 NewRunnableMethod(this, &AsynchronousPolicyLoader::ReloadFromTask); 109 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, 110 delay.InMilliseconds()); 111 } 112 113 void AsynchronousPolicyLoader::ScheduleFallbackReloadTask() { 114 // As a safeguard in case that the load delegate failed to timely notice a 115 // change in policy, schedule a reload task that'll make us recheck after a 116 // reasonable interval. 117 ScheduleReloadTask(reload_interval_); 118 } 119 120 void AsynchronousPolicyLoader::ReloadFromTask() { 121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 122 123 // Drop the reference to the reload task, since the task might be the only 124 // referrer that keeps us alive, so we should not Cancel() it. 125 reload_task_ = NULL; 126 127 Reload(); 128 } 129 130 void AsynchronousPolicyLoader::InitOnFileThread() { 131 } 132 133 void AsynchronousPolicyLoader::StopOnFileThread() { 134 CancelReloadTask(); 135 } 136 137 void AsynchronousPolicyLoader::PostUpdatePolicyTask( 138 DictionaryValue* new_policy) { 139 origin_loop_->PostTask(FROM_HERE, new UpdatePolicyTask(this, new_policy)); 140 } 141 142 void AsynchronousPolicyLoader::UpdatePolicy(DictionaryValue* new_policy_raw) { 143 scoped_ptr<DictionaryValue> new_policy(new_policy_raw); 144 DCHECK(policy_.get()); 145 if (!policy_->Equals(new_policy.get())) { 146 policy_.reset(new_policy.release()); 147 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer, 148 observer_list_, 149 OnUpdatePolicy()); 150 } 151 } 152 153 void AsynchronousPolicyLoader::InitAfterFileThreadAvailable() { 154 if (!stopped_) { 155 BrowserThread::PostTask( 156 BrowserThread::FILE, FROM_HERE, 157 NewRunnableMethod(this, &AsynchronousPolicyLoader::InitOnFileThread)); 158 } 159 } 160 161 } // namespace policy 162