cros_settings.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/chromeos/settings/cros_settings.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "base/stl_util.h"
11#include "base/string_util.h"
12#include "base/values.h"
13#include "chrome/browser/chromeos/settings/device_settings_provider.h"
14#include "chrome/browser/chromeos/settings/device_settings_service.h"
15#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
16#include "chrome/browser/chromeos/settings/system_settings_provider.h"
17#include "chrome/common/chrome_notification_types.h"
18#include "chrome/common/chrome_switches.h"
19#include "content/public/browser/notification_details.h"
20#include "content/public/browser/notification_source.h"
21#include "content/public/browser/notification_types.h"
22#include "google_apis/gaia/gaia_auth_util.h"
23
24namespace chromeos {
25
26static CrosSettings*  g_cros_settings = NULL;
27
28// static
29void CrosSettings::Initialize() {
30  CHECK(!g_cros_settings);
31  g_cros_settings = new CrosSettings();
32}
33
34// static
35bool CrosSettings::IsInitialized() {
36  return g_cros_settings;
37}
38
39// static
40void CrosSettings::Shutdown() {
41  DCHECK(g_cros_settings);
42  delete g_cros_settings;
43  g_cros_settings = NULL;
44}
45
46// static
47CrosSettings* CrosSettings::Get() {
48  CHECK(g_cros_settings);
49  return g_cros_settings;
50}
51
52bool CrosSettings::IsCrosSettings(const std::string& path) {
53  return StartsWithASCII(path, kCrosSettingsPrefix, true);
54}
55
56void CrosSettings::Set(const std::string& path, const base::Value& in_value) {
57  DCHECK(CalledOnValidThread());
58  CrosSettingsProvider* provider;
59  provider = GetProvider(path);
60  if (provider)
61    provider->Set(path, in_value);
62}
63
64const base::Value* CrosSettings::GetPref(const std::string& path) const {
65  DCHECK(CalledOnValidThread());
66  CrosSettingsProvider* provider = GetProvider(path);
67  if (provider)
68    return provider->Get(path);
69  NOTREACHED() << path << " preference was not found in the signed settings.";
70  return NULL;
71}
72
73CrosSettingsProvider::TrustedStatus CrosSettings::PrepareTrustedValues(
74    const base::Closure& callback) const {
75  DCHECK(CalledOnValidThread());
76  for (size_t i = 0; i < providers_.size(); ++i) {
77    CrosSettingsProvider::TrustedStatus status =
78        providers_[i]->PrepareTrustedValues(callback);
79    if (status != CrosSettingsProvider::TRUSTED)
80      return status;
81  }
82  return CrosSettingsProvider::TRUSTED;
83}
84
85void CrosSettings::SetBoolean(const std::string& path, bool in_value) {
86  DCHECK(CalledOnValidThread());
87  base::FundamentalValue value(in_value);
88  Set(path, value);
89}
90
91void CrosSettings::SetInteger(const std::string& path, int in_value) {
92  DCHECK(CalledOnValidThread());
93  base::FundamentalValue value(in_value);
94  Set(path, value);
95}
96
97void CrosSettings::SetDouble(const std::string& path, double in_value) {
98  DCHECK(CalledOnValidThread());
99  base::FundamentalValue value(in_value);
100  Set(path, value);
101}
102
103void CrosSettings::SetString(const std::string& path,
104                             const std::string& in_value) {
105  DCHECK(CalledOnValidThread());
106  base::StringValue value(in_value);
107  Set(path, value);
108}
109
110void CrosSettings::AppendToList(const std::string& path,
111                                const base::Value* value) {
112  DCHECK(CalledOnValidThread());
113  const base::Value* old_value = GetPref(path);
114  scoped_ptr<base::Value> new_value(
115      old_value ? old_value->DeepCopy() : new base::ListValue());
116  static_cast<base::ListValue*>(new_value.get())->Append(value->DeepCopy());
117  Set(path, *new_value);
118}
119
120void CrosSettings::RemoveFromList(const std::string& path,
121                                  const base::Value* value) {
122  DCHECK(CalledOnValidThread());
123  const base::Value* old_value = GetPref(path);
124  scoped_ptr<base::Value> new_value(
125      old_value ? old_value->DeepCopy() : new base::ListValue());
126  static_cast<base::ListValue*>(new_value.get())->Remove(*value, NULL);
127  Set(path, *new_value);
128}
129
130bool CrosSettings::GetBoolean(const std::string& path,
131                              bool* bool_value) const {
132  DCHECK(CalledOnValidThread());
133  const base::Value* value = GetPref(path);
134  if (value)
135    return value->GetAsBoolean(bool_value);
136  return false;
137}
138
139bool CrosSettings::GetInteger(const std::string& path,
140                              int* out_value) const {
141  DCHECK(CalledOnValidThread());
142  const base::Value* value = GetPref(path);
143  if (value)
144    return value->GetAsInteger(out_value);
145  return false;
146}
147
148bool CrosSettings::GetDouble(const std::string& path,
149                             double* out_value) const {
150  DCHECK(CalledOnValidThread());
151  const base::Value* value = GetPref(path);
152  if (value)
153    return value->GetAsDouble(out_value);
154  return false;
155}
156
157bool CrosSettings::GetString(const std::string& path,
158                             std::string* out_value) const {
159  DCHECK(CalledOnValidThread());
160  const base::Value* value = GetPref(path);
161  if (value)
162    return value->GetAsString(out_value);
163  return false;
164}
165
166bool CrosSettings::GetList(const std::string& path,
167                           const base::ListValue** out_value) const {
168  DCHECK(CalledOnValidThread());
169  const base::Value* value = GetPref(path);
170  if (value)
171    return value->GetAsList(out_value);
172  return false;
173}
174
175bool CrosSettings::GetDictionary(
176    const std::string& path,
177    const base::DictionaryValue** out_value) const {
178  DCHECK(CalledOnValidThread());
179  const base::Value* value = GetPref(path);
180  if (value)
181    return value->GetAsDictionary(out_value);
182  return false;
183}
184
185bool CrosSettings::FindEmailInList(const std::string& path,
186                                   const std::string& email) const {
187  DCHECK(CalledOnValidThread());
188  std::string canonicalized_email(
189      gaia::CanonicalizeEmail(gaia::SanitizeEmail(email)));
190  std::string wildcard_email;
191  std::string::size_type at_pos = canonicalized_email.find('@');
192  if (at_pos != std::string::npos) {
193    wildcard_email =
194        std::string("*").append(canonicalized_email.substr(at_pos));
195  }
196
197  const base::ListValue* list;
198  if (!GetList(path, &list))
199    return false;
200  for (base::ListValue::const_iterator entry(list->begin());
201       entry != list->end();
202       ++entry) {
203    std::string entry_string;
204    if (!(*entry)->GetAsString(&entry_string)) {
205      NOTREACHED();
206      continue;
207    }
208    std::string canonicalized_entry(
209        gaia::CanonicalizeEmail(gaia::SanitizeEmail(entry_string)));
210
211    if (canonicalized_entry == canonicalized_email ||
212        canonicalized_entry == wildcard_email) {
213      return true;
214    }
215  }
216  return false;
217}
218
219bool CrosSettings::AddSettingsProvider(CrosSettingsProvider* provider) {
220  DCHECK(CalledOnValidThread());
221  providers_.push_back(provider);
222
223  // Allow the provider to notify this object when settings have changed.
224  // Providers instantiated inside this class will have the same callback
225  // passed to their constructor, but doing it here allows for providers
226  // to be instantiated outside this class.
227  CrosSettingsProvider::NotifyObserversCallback notify_cb(
228      base::Bind(&CrosSettings::FireObservers, base::Unretained(this)));
229  provider->SetNotifyObserversCallback(notify_cb);
230  return true;
231}
232
233bool CrosSettings::RemoveSettingsProvider(CrosSettingsProvider* provider) {
234  DCHECK(CalledOnValidThread());
235  std::vector<CrosSettingsProvider*>::iterator it =
236      std::find(providers_.begin(), providers_.end(), provider);
237  if (it != providers_.end()) {
238    providers_.erase(it);
239    return true;
240  }
241  return false;
242}
243
244void CrosSettings::AddSettingsObserver(const char* path,
245                                       content::NotificationObserver* obs) {
246  DCHECK(path);
247  DCHECK(obs);
248  DCHECK(CalledOnValidThread());
249
250  if (!GetProvider(std::string(path))) {
251    NOTREACHED() << "Trying to add an observer for an unregistered setting: "
252                 << path;
253    return;
254  }
255
256  // Get the settings observer list associated with the path.
257  NotificationObserverList* observer_list = NULL;
258  SettingsObserverMap::iterator observer_iterator =
259      settings_observers_.find(path);
260  if (observer_iterator == settings_observers_.end()) {
261    observer_list = new NotificationObserverList;
262    settings_observers_[path] = observer_list;
263  } else {
264    observer_list = observer_iterator->second;
265  }
266
267  // Verify that this observer doesn't already exist.
268  NotificationObserverList::Iterator it(*observer_list);
269  content::NotificationObserver* existing_obs;
270  while ((existing_obs = it.GetNext()) != NULL) {
271    if (existing_obs == obs)
272      return;
273  }
274
275  // Ok, safe to add the pref observer.
276  observer_list->AddObserver(obs);
277}
278
279void CrosSettings::RemoveSettingsObserver(const char* path,
280                                          content::NotificationObserver* obs) {
281  DCHECK(CalledOnValidThread());
282
283  SettingsObserverMap::iterator observer_iterator =
284      settings_observers_.find(path);
285  if (observer_iterator == settings_observers_.end())
286    return;
287
288  NotificationObserverList* observer_list = observer_iterator->second;
289  observer_list->RemoveObserver(obs);
290}
291
292CrosSettingsProvider* CrosSettings::GetProvider(
293    const std::string& path) const {
294  for (size_t i = 0; i < providers_.size(); ++i) {
295    if (providers_[i]->HandlesSetting(path))
296      return providers_[i];
297  }
298  return NULL;
299}
300
301CrosSettings::CrosSettings() {
302  CrosSettingsProvider::NotifyObserversCallback notify_cb(
303      base::Bind(&CrosSettings::FireObservers,
304                 // This is safe since |this| is never deleted.
305                 base::Unretained(this)));
306  if (CommandLine::ForCurrentProcess()->HasSwitch(
307          switches::kStubCrosSettings)) {
308    AddSettingsProvider(new StubCrosSettingsProvider(notify_cb));
309  } else {
310    AddSettingsProvider(
311        new DeviceSettingsProvider(notify_cb, DeviceSettingsService::Get()));
312  }
313  // System settings are not mocked currently.
314  AddSettingsProvider(new SystemSettingsProvider(notify_cb));
315}
316
317CrosSettings::~CrosSettings() {
318  STLDeleteElements(&providers_);
319  STLDeleteValues(&settings_observers_);
320}
321
322void CrosSettings::FireObservers(const std::string& path) {
323  DCHECK(CalledOnValidThread());
324  SettingsObserverMap::iterator observer_iterator =
325      settings_observers_.find(path);
326  if (observer_iterator == settings_observers_.end())
327    return;
328
329  NotificationObserverList::Iterator it(*(observer_iterator->second));
330  content::NotificationObserver* observer;
331  while ((observer = it.GetNext()) != NULL) {
332    observer->Observe(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED,
333                      content::Source<CrosSettings>(this),
334                      content::Details<const std::string>(&path));
335  }
336}
337
338ScopedTestCrosSettings::ScopedTestCrosSettings() {
339  CrosSettings::Initialize();
340}
341
342ScopedTestCrosSettings::~ScopedTestCrosSettings() {
343  CrosSettings::Shutdown();
344}
345
346}  // namespace chromeos
347