summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy/async_policy_provider.cc
blob: 26b61b3cee3186155c49c0e73046e51db56cbce3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) 2012 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/async_policy_provider.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chrome/browser/policy/async_policy_loader.h"
#include "chrome/browser/policy/policy_bundle.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace policy {

AsyncPolicyProvider::AsyncPolicyProvider(scoped_ptr<AsyncPolicyLoader> loader)
    : loader_(loader.release()),
      weak_factory_(this) {
  // Make an immediate synchronous load on startup.
  OnLoaderReloaded(loader_->InitialLoad());
}

AsyncPolicyProvider::~AsyncPolicyProvider() {
  DCHECK(CalledOnValidThread());
  // Shutdown() must have been called before.
  DCHECK(!loader_);
}

void AsyncPolicyProvider::Init() {
  DCHECK(CalledOnValidThread());
  ConfigurationPolicyProvider::Init();

  if (!loader_)
    return;

  AsyncPolicyLoader::UpdateCallback callback =
      base::Bind(&AsyncPolicyProvider::LoaderUpdateCallback,
                 base::MessageLoopProxy::current(),
                 weak_factory_.GetWeakPtr());
  bool post = BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&AsyncPolicyLoader::Init,
                 base::Unretained(loader_),
                 callback));
  DCHECK(post) << "AsyncPolicyProvider::Init() called with threads not running";
}

void AsyncPolicyProvider::Shutdown() {
  DCHECK(CalledOnValidThread());
  // Note on the lifetime of |loader_|:
  // The |loader_| lives on the FILE thread, and is deleted from here. This
  // means that posting tasks on the |loader_| to FILE from the
  // AsyncPolicyProvider is always safe, since a potential DeleteSoon() is only
  // posted from here. The |loader_| posts back to the AsyncPolicyProvider
  // through the |update_callback_|, which has a WeakPtr to |this|.
  if (!BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, loader_)) {
    // The FILE thread doesn't exist; this only happens on unit tests.
    delete loader_;
  }
  loader_ = NULL;
  ConfigurationPolicyProvider::Shutdown();
}

void AsyncPolicyProvider::RefreshPolicies() {
  DCHECK(CalledOnValidThread());

  // Subtle: RefreshPolicies() has a contract that requires the next policy
  // update notification (triggered from UpdatePolicy()) to reflect any changes
  // made before this call. So if a caller has modified the policy settings and
  // invoked RefreshPolicies(), then by the next notification these policies
  // should already be provided.
  // However, it's also possible that an asynchronous Reload() is in progress
  // and just posted OnLoaderReloaded(). Therefore a task is posted to the
  // FILE thread before posting the next Reload, to prevent a potential
  // concurrent Reload() from triggering a notification too early. If another
  // refresh task has been posted, it is invalidated now.
  refresh_callback_.Reset(
      base::Bind(&AsyncPolicyProvider::ReloadAfterRefreshSync,
                 base::Unretained(this)));
  BrowserThread::PostTaskAndReply(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(base::DoNothing),
      refresh_callback_.callback());
}

void AsyncPolicyProvider::ReloadAfterRefreshSync() {
  DCHECK(CalledOnValidThread());
  // This task can only enter if it was posted from RefreshPolicies(), and it
  // hasn't been cancelled meanwhile by another call to RefreshPolicies().
  DCHECK(!refresh_callback_.IsCancelled());
  // There can't be another refresh callback pending now, since its creation
  // in RefreshPolicies() would have cancelled the current execution. So it's
  // safe to cancel the |refresh_callback_| now, so that OnLoaderReloaded()
  // sees that there is no refresh pending.
  refresh_callback_.Cancel();

  if (!loader_)
    return;

  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&AsyncPolicyLoader::Reload,
                 base::Unretained(loader_),
                 true  /* force */));
}

void AsyncPolicyProvider::OnLoaderReloaded(scoped_ptr<PolicyBundle> bundle) {
  DCHECK(CalledOnValidThread());
  // Only propagate policy updates if there are no pending refreshes, and if
  // Shutdown() hasn't been called yet.
  if (refresh_callback_.IsCancelled() && loader_)
    UpdatePolicy(bundle.Pass());
}

// static
void AsyncPolicyProvider::LoaderUpdateCallback(
    scoped_refptr<base::MessageLoopProxy> loop,
    base::WeakPtr<AsyncPolicyProvider> weak_this,
    scoped_ptr<PolicyBundle> bundle) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  loop->PostTask(FROM_HERE,
                 base::Bind(&AsyncPolicyProvider::OnLoaderReloaded,
                            weak_this,
                            base::Passed(&bundle)));
}

}  // namespace policy