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