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 "base/prefs/pref_value_store.h"
6
7#include "base/logging.h"
8#include "base/prefs/pref_notifier.h"
9#include "base/prefs/pref_observer.h"
10
11PrefValueStore::PrefStoreKeeper::PrefStoreKeeper()
12    : pref_value_store_(NULL),
13      type_(PrefValueStore::INVALID_STORE) {
14}
15
16PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() {
17  if (pref_store_.get()) {
18    pref_store_->RemoveObserver(this);
19    pref_store_ = NULL;
20  }
21  pref_value_store_ = NULL;
22}
23
24void PrefValueStore::PrefStoreKeeper::Initialize(
25    PrefValueStore* store,
26    PrefStore* pref_store,
27    PrefValueStore::PrefStoreType type) {
28  if (pref_store_.get()) {
29    pref_store_->RemoveObserver(this);
30    DCHECK(!pref_store_->HasObservers());
31  }
32  type_ = type;
33  pref_value_store_ = store;
34  pref_store_ = pref_store;
35  if (pref_store_.get())
36    pref_store_->AddObserver(this);
37}
38
39void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged(
40    const std::string& key) {
41  pref_value_store_->OnPrefValueChanged(type_, key);
42}
43
44void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted(
45    bool succeeded) {
46  pref_value_store_->OnInitializationCompleted(type_, succeeded);
47}
48
49PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
50                               PrefStore* supervised_user_prefs,
51                               PrefStore* extension_prefs,
52                               PrefStore* command_line_prefs,
53                               PrefStore* user_prefs,
54                               PrefStore* recommended_prefs,
55                               PrefStore* default_prefs,
56                               PrefNotifier* pref_notifier)
57    : pref_notifier_(pref_notifier),
58      initialization_failed_(false) {
59  InitPrefStore(MANAGED_STORE, managed_prefs);
60  InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs);
61  InitPrefStore(EXTENSION_STORE, extension_prefs);
62  InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
63  InitPrefStore(USER_STORE, user_prefs);
64  InitPrefStore(RECOMMENDED_STORE, recommended_prefs);
65  InitPrefStore(DEFAULT_STORE, default_prefs);
66
67  CheckInitializationCompleted();
68}
69
70PrefValueStore::~PrefValueStore() {}
71
72PrefValueStore* PrefValueStore::CloneAndSpecialize(
73    PrefStore* managed_prefs,
74    PrefStore* supervised_user_prefs,
75    PrefStore* extension_prefs,
76    PrefStore* command_line_prefs,
77    PrefStore* user_prefs,
78    PrefStore* recommended_prefs,
79    PrefStore* default_prefs,
80    PrefNotifier* pref_notifier) {
81  DCHECK(pref_notifier);
82  if (!managed_prefs)
83    managed_prefs = GetPrefStore(MANAGED_STORE);
84  if (!supervised_user_prefs)
85    supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE);
86  if (!extension_prefs)
87    extension_prefs = GetPrefStore(EXTENSION_STORE);
88  if (!command_line_prefs)
89    command_line_prefs = GetPrefStore(COMMAND_LINE_STORE);
90  if (!user_prefs)
91    user_prefs = GetPrefStore(USER_STORE);
92  if (!recommended_prefs)
93    recommended_prefs = GetPrefStore(RECOMMENDED_STORE);
94  if (!default_prefs)
95    default_prefs = GetPrefStore(DEFAULT_STORE);
96
97  return new PrefValueStore(
98      managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs,
99      user_prefs, recommended_prefs, default_prefs, pref_notifier);
100}
101
102void PrefValueStore::set_callback(const PrefChangedCallback& callback) {
103  pref_changed_callback_ = callback;
104}
105
106bool PrefValueStore::GetValue(const std::string& name,
107                              base::Value::Type type,
108                              const base::Value** out_value) const {
109  // Check the |PrefStore|s in order of their priority from highest to lowest,
110  // looking for the first preference value with the given |name| and |type|.
111  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
112    if (GetValueFromStoreWithType(name.c_str(), type,
113                                  static_cast<PrefStoreType>(i), out_value))
114      return true;
115  }
116  return false;
117}
118
119bool PrefValueStore::GetRecommendedValue(const std::string& name,
120                                         base::Value::Type type,
121                                         const base::Value** out_value) const {
122  return GetValueFromStoreWithType(name.c_str(), type, RECOMMENDED_STORE,
123                                   out_value);
124}
125
126void PrefValueStore::NotifyPrefChanged(
127    const char* path,
128    PrefValueStore::PrefStoreType new_store) {
129  DCHECK(new_store != INVALID_STORE);
130  // A notification is sent when the pref value in any store changes. If this
131  // store is currently being overridden by a higher-priority store, the
132  // effective value of the pref will not have changed.
133  pref_notifier_->OnPreferenceChanged(path);
134  if (!pref_changed_callback_.is_null())
135    pref_changed_callback_.Run(path);
136}
137
138bool PrefValueStore::PrefValueInManagedStore(const char* name) const {
139  return PrefValueInStore(name, MANAGED_STORE);
140}
141
142bool PrefValueStore::PrefValueInExtensionStore(const char* name) const {
143  return PrefValueInStore(name, EXTENSION_STORE);
144}
145
146bool PrefValueStore::PrefValueInUserStore(const char* name) const {
147  return PrefValueInStore(name, USER_STORE);
148}
149
150bool PrefValueStore::PrefValueFromExtensionStore(const char* name) const {
151  return ControllingPrefStoreForPref(name) == EXTENSION_STORE;
152}
153
154bool PrefValueStore::PrefValueFromUserStore(const char* name) const {
155  return ControllingPrefStoreForPref(name) == USER_STORE;
156}
157
158bool PrefValueStore::PrefValueFromRecommendedStore(const char* name) const {
159  return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE;
160}
161
162bool PrefValueStore::PrefValueFromDefaultStore(const char* name) const {
163  return ControllingPrefStoreForPref(name) == DEFAULT_STORE;
164}
165
166bool PrefValueStore::PrefValueUserModifiable(const char* name) const {
167  PrefStoreType effective_store = ControllingPrefStoreForPref(name);
168  return effective_store >= USER_STORE ||
169         effective_store == INVALID_STORE;
170}
171
172bool PrefValueStore::PrefValueExtensionModifiable(const char* name) const {
173  PrefStoreType effective_store = ControllingPrefStoreForPref(name);
174  return effective_store >= EXTENSION_STORE ||
175         effective_store == INVALID_STORE;
176}
177
178void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) {
179  InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
180}
181
182bool PrefValueStore::PrefValueInStore(
183    const char* name,
184    PrefValueStore::PrefStoreType store) const {
185  // Declare a temp Value* and call GetValueFromStore,
186  // ignoring the output value.
187  const base::Value* tmp_value = NULL;
188  return GetValueFromStore(name, store, &tmp_value);
189}
190
191bool PrefValueStore::PrefValueInStoreRange(
192    const char* name,
193    PrefValueStore::PrefStoreType first_checked_store,
194    PrefValueStore::PrefStoreType last_checked_store) const {
195  if (first_checked_store > last_checked_store) {
196    NOTREACHED();
197    return false;
198  }
199
200  for (size_t i = first_checked_store;
201       i <= static_cast<size_t>(last_checked_store); ++i) {
202    if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
203      return true;
204  }
205  return false;
206}
207
208PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref(
209    const char* name) const {
210  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
211    if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
212      return static_cast<PrefStoreType>(i);
213  }
214  return INVALID_STORE;
215}
216
217bool PrefValueStore::GetValueFromStore(const char* name,
218                                       PrefValueStore::PrefStoreType store_type,
219                                       const base::Value** out_value) const {
220  // Only return true if we find a value and it is the correct type, so stale
221  // values with the incorrect type will be ignored.
222  const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type));
223  if (store && store->GetValue(name, out_value))
224    return true;
225
226  // No valid value found for the given preference name: set the return value
227  // to false.
228  *out_value = NULL;
229  return false;
230}
231
232bool PrefValueStore::GetValueFromStoreWithType(
233    const char* name,
234    base::Value::Type type,
235    PrefStoreType store,
236    const base::Value** out_value) const {
237  if (GetValueFromStore(name, store, out_value)) {
238    if ((*out_value)->IsType(type))
239      return true;
240
241    LOG(WARNING) << "Expected type for " << name << " is " << type
242                 << " but got " << (*out_value)->GetType()
243                 << " in store " << store;
244  }
245
246  *out_value = NULL;
247  return false;
248}
249
250void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type,
251                                        const std::string& key) {
252  NotifyPrefChanged(key.c_str(), type);
253}
254
255void PrefValueStore::OnInitializationCompleted(
256    PrefValueStore::PrefStoreType type, bool succeeded) {
257  if (initialization_failed_)
258    return;
259  if (!succeeded) {
260    initialization_failed_ = true;
261    pref_notifier_->OnInitializationCompleted(false);
262    return;
263  }
264  CheckInitializationCompleted();
265}
266
267void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type,
268                                   PrefStore* pref_store) {
269  pref_stores_[type].Initialize(this, pref_store, type);
270}
271
272void PrefValueStore::CheckInitializationCompleted() {
273  if (initialization_failed_)
274    return;
275  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
276    scoped_refptr<PrefStore> store =
277        GetPrefStore(static_cast<PrefStoreType>(i));
278    if (store.get() && !store->IsInitializationComplete())
279      return;
280  }
281  pref_notifier_->OnInitializationCompleted(true);
282}
283