Home | History | Annotate | Download | only in policy
      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