1 // Copyright (c) 2012 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/async_policy_provider.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/message_loop/message_loop_proxy.h" 11 #include "chrome/browser/policy/async_policy_loader.h" 12 #include "chrome/browser/policy/policy_bundle.h" 13 #include "chrome/browser/policy/policy_domain_descriptor.h" 14 #include "content/public/browser/browser_thread.h" 15 16 using content::BrowserThread; 17 18 namespace policy { 19 20 AsyncPolicyProvider::AsyncPolicyProvider(scoped_ptr<AsyncPolicyLoader> loader) 21 : loader_(loader.release()), 22 weak_factory_(this) { 23 // Make an immediate synchronous load on startup. 24 OnLoaderReloaded(loader_->InitialLoad()); 25 } 26 27 AsyncPolicyProvider::~AsyncPolicyProvider() { 28 DCHECK(CalledOnValidThread()); 29 // Shutdown() must have been called before. 30 DCHECK(!loader_); 31 } 32 33 void AsyncPolicyProvider::Init() { 34 DCHECK(CalledOnValidThread()); 35 ConfigurationPolicyProvider::Init(); 36 37 if (!loader_) 38 return; 39 40 AsyncPolicyLoader::UpdateCallback callback = 41 base::Bind(&AsyncPolicyProvider::LoaderUpdateCallback, 42 base::MessageLoopProxy::current(), 43 weak_factory_.GetWeakPtr()); 44 bool post = BrowserThread::PostTask( 45 BrowserThread::FILE, FROM_HERE, 46 base::Bind(&AsyncPolicyLoader::Init, 47 base::Unretained(loader_), 48 callback)); 49 DCHECK(post) << "AsyncPolicyProvider::Init() called with threads not running"; 50 } 51 52 void AsyncPolicyProvider::Shutdown() { 53 DCHECK(CalledOnValidThread()); 54 // Note on the lifetime of |loader_|: 55 // The |loader_| lives on the FILE thread, and is deleted from here. This 56 // means that posting tasks on the |loader_| to FILE from the 57 // AsyncPolicyProvider is always safe, since a potential DeleteSoon() is only 58 // posted from here. The |loader_| posts back to the AsyncPolicyProvider 59 // through the |update_callback_|, which has a WeakPtr to |this|. 60 if (!BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, loader_)) { 61 // The FILE thread doesn't exist; this only happens on unit tests. 62 delete loader_; 63 } 64 loader_ = NULL; 65 ConfigurationPolicyProvider::Shutdown(); 66 } 67 68 void AsyncPolicyProvider::RefreshPolicies() { 69 DCHECK(CalledOnValidThread()); 70 71 // Subtle: RefreshPolicies() has a contract that requires the next policy 72 // update notification (triggered from UpdatePolicy()) to reflect any changes 73 // made before this call. So if a caller has modified the policy settings and 74 // invoked RefreshPolicies(), then by the next notification these policies 75 // should already be provided. 76 // However, it's also possible that an asynchronous Reload() is in progress 77 // and just posted OnLoaderReloaded(). Therefore a task is posted to the 78 // FILE thread before posting the next Reload, to prevent a potential 79 // concurrent Reload() from triggering a notification too early. If another 80 // refresh task has been posted, it is invalidated now. 81 refresh_callback_.Reset( 82 base::Bind(&AsyncPolicyProvider::ReloadAfterRefreshSync, 83 weak_factory_.GetWeakPtr())); 84 BrowserThread::PostTaskAndReply( 85 BrowserThread::FILE, FROM_HERE, 86 base::Bind(base::DoNothing), 87 refresh_callback_.callback()); 88 } 89 90 void AsyncPolicyProvider::RegisterPolicyDomain( 91 scoped_refptr<const PolicyDomainDescriptor> descriptor) { 92 if (loader_) { 93 BrowserThread::PostTask(BrowserThread::FILE, 94 FROM_HERE, 95 base::Bind(&AsyncPolicyLoader::RegisterPolicyDomain, 96 base::Unretained(loader_), 97 descriptor)); 98 } 99 } 100 101 void AsyncPolicyProvider::ReloadAfterRefreshSync() { 102 DCHECK(CalledOnValidThread()); 103 // This task can only enter if it was posted from RefreshPolicies(), and it 104 // hasn't been cancelled meanwhile by another call to RefreshPolicies(). 105 DCHECK(!refresh_callback_.IsCancelled()); 106 // There can't be another refresh callback pending now, since its creation 107 // in RefreshPolicies() would have cancelled the current execution. So it's 108 // safe to cancel the |refresh_callback_| now, so that OnLoaderReloaded() 109 // sees that there is no refresh pending. 110 refresh_callback_.Cancel(); 111 112 if (!loader_) 113 return; 114 115 BrowserThread::PostTask( 116 BrowserThread::FILE, FROM_HERE, 117 base::Bind(&AsyncPolicyLoader::Reload, 118 base::Unretained(loader_), 119 true /* force */)); 120 } 121 122 void AsyncPolicyProvider::OnLoaderReloaded(scoped_ptr<PolicyBundle> bundle) { 123 DCHECK(CalledOnValidThread()); 124 // Only propagate policy updates if there are no pending refreshes, and if 125 // Shutdown() hasn't been called yet. 126 if (refresh_callback_.IsCancelled() && loader_) 127 UpdatePolicy(bundle.Pass()); 128 } 129 130 // static 131 void AsyncPolicyProvider::LoaderUpdateCallback( 132 scoped_refptr<base::MessageLoopProxy> loop, 133 base::WeakPtr<AsyncPolicyProvider> weak_this, 134 scoped_ptr<PolicyBundle> bundle) { 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 136 loop->PostTask(FROM_HERE, 137 base::Bind(&AsyncPolicyProvider::OnLoaderReloaded, 138 weak_this, 139 base::Passed(&bundle))); 140 } 141 142 } // namespace policy 143