1// Copyright (c) 2011 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/ui/webui/options/core_options_handler.h"
6
7#include "base/json/json_reader.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/string16.h"
10#include "base/string_number_conversions.h"
11#include "base/utf_string_conversions.h"
12#include "base/values.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/google/google_util.h"
15#include "chrome/browser/metrics/user_metrics.h"
16#include "chrome/browser/prefs/pref_service.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/common/url_constants.h"
20#include "content/common/notification_details.h"
21#include "content/common/notification_type.h"
22#include "googleurl/src/gurl.h"
23#include "grit/chromium_strings.h"
24#include "grit/generated_resources.h"
25#include "grit/locale_settings.h"
26#include "grit/theme_resources.h"
27#include "ui/base/l10n/l10n_util.h"
28
29CoreOptionsHandler::CoreOptionsHandler()
30    : handlers_host_(NULL) {
31}
32
33CoreOptionsHandler::~CoreOptionsHandler() {}
34
35void CoreOptionsHandler::Initialize() {
36  clear_plugin_lso_data_enabled_.Init(prefs::kClearPluginLSODataEnabled,
37                                      g_browser_process->local_state(),
38                                      this);
39  UpdateClearPluginLSOData();
40}
41
42void CoreOptionsHandler::GetLocalizedValues(
43    DictionaryValue* localized_strings) {
44  DCHECK(localized_strings);
45  // Main
46  localized_strings->SetString("title",
47      l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
48
49  // Managed prefs
50  localized_strings->SetString("managedPrefsBannerText",
51      l10n_util::GetStringUTF16(IDS_OPTIONS_MANAGED_PREFS));
52
53  // Search
54  RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
55  localized_strings->SetString("searchPlaceholder",
56      l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
57  localized_strings->SetString("searchPageNoMatches",
58      l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
59  localized_strings->SetString("searchPageHelpLabel",
60      l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
61  localized_strings->SetString("searchPageHelpTitle",
62      l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
63          l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
64  localized_strings->SetString("searchPageHelpURL",
65      google_util::AppendGoogleLocaleParam(
66          GURL(chrome::kChromeHelpURL)).spec());
67
68  // Common
69  localized_strings->SetString("ok",
70      l10n_util::GetStringUTF16(IDS_OK));
71  localized_strings->SetString("cancel",
72      l10n_util::GetStringUTF16(IDS_CANCEL));
73  localized_strings->SetString("learnMore",
74      l10n_util::GetStringUTF16(IDS_LEARN_MORE));
75  localized_strings->SetString("close",
76      l10n_util::GetStringUTF16(IDS_CLOSE));
77}
78
79void CoreOptionsHandler::Uninitialize() {
80  std::string last_pref;
81  for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
82       iter != pref_callback_map_.end();
83       ++iter) {
84    if (last_pref != iter->first) {
85      StopObservingPref(iter->first);
86      last_pref = iter->first;
87    }
88  }
89}
90
91WebUIMessageHandler* CoreOptionsHandler::Attach(WebUI* web_ui) {
92  WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
93  DCHECK(web_ui_);
94  registrar_.Init(web_ui_->GetProfile()->GetPrefs());
95  return result;
96}
97
98void CoreOptionsHandler::Observe(NotificationType type,
99                                 const NotificationSource& source,
100                                 const NotificationDetails& details) {
101  if (type == NotificationType::PREF_CHANGED)
102    NotifyPrefChanged(Details<std::string>(details).ptr());
103}
104
105void CoreOptionsHandler::RegisterMessages() {
106  web_ui_->RegisterMessageCallback("coreOptionsInitialize",
107      NewCallback(this, &CoreOptionsHandler::HandleInitialize));
108  web_ui_->RegisterMessageCallback("fetchPrefs",
109      NewCallback(this, &CoreOptionsHandler::HandleFetchPrefs));
110  web_ui_->RegisterMessageCallback("observePrefs",
111      NewCallback(this, &CoreOptionsHandler::HandleObservePrefs));
112  web_ui_->RegisterMessageCallback("setBooleanPref",
113      NewCallback(this, &CoreOptionsHandler::HandleSetBooleanPref));
114  web_ui_->RegisterMessageCallback("setIntegerPref",
115      NewCallback(this, &CoreOptionsHandler::HandleSetIntegerPref));
116  web_ui_->RegisterMessageCallback("setDoublePref",
117      NewCallback(this, &CoreOptionsHandler::HandleSetDoublePref));
118  web_ui_->RegisterMessageCallback("setStringPref",
119      NewCallback(this, &CoreOptionsHandler::HandleSetStringPref));
120  web_ui_->RegisterMessageCallback("setListPref",
121      NewCallback(this, &CoreOptionsHandler::HandleSetListPref));
122  web_ui_->RegisterMessageCallback("clearPref",
123      NewCallback(this, &CoreOptionsHandler::HandleClearPref));
124  web_ui_->RegisterMessageCallback("coreOptionsUserMetricsAction",
125      NewCallback(this, &CoreOptionsHandler::HandleUserMetricsAction));
126}
127
128void CoreOptionsHandler::HandleInitialize(const ListValue* args) {
129  DCHECK(handlers_host_);
130  handlers_host_->InitializeHandlers();
131}
132
133Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
134  PrefService* pref_service = web_ui_->GetProfile()->GetPrefs();
135
136  const PrefService::Preference* pref =
137      pref_service->FindPreference(pref_name.c_str());
138
139  Value* return_value;
140  if (pref) {
141    DictionaryValue* dict = new DictionaryValue;
142    dict->Set("value", pref->GetValue()->DeepCopy());
143    dict->SetBoolean("managed", pref->IsManaged());
144    return_value = dict;
145  } else {
146    return_value = Value::CreateNullValue();
147  }
148  return return_value;
149}
150
151void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
152  registrar_.Add(pref_name.c_str(), this);
153}
154
155void CoreOptionsHandler::SetPref(const std::string& pref_name,
156                                 const Value* value,
157                                 const std::string& metric) {
158  PrefService* pref_service = web_ui_->GetProfile()->GetPrefs();
159
160  switch (value->GetType()) {
161    case Value::TYPE_BOOLEAN:
162    case Value::TYPE_INTEGER:
163    case Value::TYPE_DOUBLE:
164    case Value::TYPE_STRING:
165      pref_service->Set(pref_name.c_str(), *value);
166      break;
167
168    default:
169      NOTREACHED();
170      return;
171  }
172
173  pref_service->ScheduleSavePersistentPrefs();
174  ProcessUserMetric(value, metric);
175}
176
177void CoreOptionsHandler::ClearPref(const std::string& pref_name,
178                                   const std::string& metric) {
179  PrefService* pref_service = web_ui_->GetProfile()->GetPrefs();
180  pref_service->ClearPref(pref_name.c_str());
181  pref_service->ScheduleSavePersistentPrefs();
182
183  if (!metric.empty())
184    UserMetricsRecordAction(UserMetricsAction(metric.c_str()));
185}
186
187void CoreOptionsHandler::ProcessUserMetric(const Value* value,
188                                           const std::string& metric) {
189  if (metric.empty())
190    return;
191
192  std::string metric_string = metric;
193  if (value->IsType(Value::TYPE_BOOLEAN)) {
194    bool bool_value;
195    CHECK(value->GetAsBoolean(&bool_value));
196    metric_string += bool_value ? "_Enable" : "_Disable";
197  }
198
199  UserMetricsRecordAction(UserMetricsAction(metric_string.c_str()));
200}
201
202void CoreOptionsHandler::StopObservingPref(const std::string& path) {
203  registrar_.Remove(path.c_str(), this);
204}
205
206void CoreOptionsHandler::HandleFetchPrefs(const ListValue* args) {
207  // First param is name of callback function, so, there needs to be at least
208  // one more element for the actual preference identifier.
209  DCHECK_GE(static_cast<int>(args->GetSize()), 2);
210
211  // Get callback JS function name.
212  Value* callback;
213  if (!args->Get(0, &callback) || !callback->IsType(Value::TYPE_STRING))
214    return;
215
216  string16 callback_function;
217  if (!callback->GetAsString(&callback_function))
218    return;
219
220  // Get the list of name for prefs to build the response dictionary.
221  DictionaryValue result_value;
222  Value* list_member;
223
224  for (size_t i = 1; i < args->GetSize(); i++) {
225    if (!args->Get(i, &list_member))
226      break;
227
228    if (!list_member->IsType(Value::TYPE_STRING))
229      continue;
230
231    std::string pref_name;
232    if (!list_member->GetAsString(&pref_name))
233      continue;
234
235    result_value.Set(pref_name.c_str(), FetchPref(pref_name));
236  }
237  web_ui_->CallJavascriptFunction(UTF16ToASCII(callback_function),
238                                  result_value);
239}
240
241void CoreOptionsHandler::HandleObservePrefs(const ListValue* args) {
242  // First param is name is JS callback function name, the rest are pref
243  // identifiers that we are observing.
244  DCHECK_GE(static_cast<int>(args->GetSize()), 2);
245
246  // Get preference change callback function name.
247  string16 callback_func_name;
248  if (!args->GetString(0, &callback_func_name))
249    return;
250
251  // Get all other parameters - pref identifiers.
252  for (size_t i = 1; i < args->GetSize(); i++) {
253    Value* list_member;
254    if (!args->Get(i, &list_member))
255      break;
256
257    // Just ignore bad pref identifiers for now.
258    std::string pref_name;
259    if (!list_member->IsType(Value::TYPE_STRING) ||
260        !list_member->GetAsString(&pref_name))
261      continue;
262
263    if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
264      ObservePref(pref_name);
265
266    pref_callback_map_.insert(
267        PreferenceCallbackMap::value_type(pref_name,
268                                          UTF16ToWideHack(callback_func_name)));
269  }
270}
271
272void CoreOptionsHandler::HandleSetBooleanPref(const ListValue* args) {
273  HandleSetPref(args, Value::TYPE_BOOLEAN);
274}
275
276void CoreOptionsHandler::HandleSetIntegerPref(const ListValue* args) {
277  HandleSetPref(args, Value::TYPE_INTEGER);
278}
279
280void CoreOptionsHandler::HandleSetDoublePref(const ListValue* args) {
281  HandleSetPref(args, Value::TYPE_DOUBLE);
282}
283
284void CoreOptionsHandler::HandleSetStringPref(const ListValue* args) {
285  HandleSetPref(args, Value::TYPE_STRING);
286}
287
288void CoreOptionsHandler::HandleSetListPref(const ListValue* args) {
289  HandleSetPref(args, Value::TYPE_LIST);
290}
291
292void CoreOptionsHandler::HandleSetPref(const ListValue* args,
293                                       Value::ValueType type) {
294  DCHECK_GT(static_cast<int>(args->GetSize()), 1);
295
296  std::string pref_name;
297  if (!args->GetString(0, &pref_name))
298    return;
299
300  Value* value;
301  if (!args->Get(1, &value))
302    return;
303
304  scoped_ptr<Value> temp_value;
305
306  // In JS all numbers are doubles.
307  if (type == Value::TYPE_INTEGER) {
308    double double_value;
309    CHECK(value->GetAsDouble(&double_value));
310    temp_value.reset(Value::CreateIntegerValue(static_cast<int>(double_value)));
311    value = temp_value.get();
312
313  // In case we have a List pref we got a JSON string.
314  } else if (type == Value::TYPE_LIST) {
315    std::string json_string;
316    CHECK(value->GetAsString(&json_string));
317    temp_value.reset(
318        base::JSONReader().JsonToValue(json_string,
319                                       false,  // no check_root
320                                       false));  // no trailing comma
321    value = temp_value.get();
322  }
323
324  CHECK_EQ(type, value->GetType());
325
326  std::string metric;
327  if (args->GetSize() > 2)
328    args->GetString(2, &metric);
329
330  SetPref(pref_name, value, metric);
331}
332
333void CoreOptionsHandler::HandleClearPref(const ListValue* args) {
334  DCHECK_GT(static_cast<int>(args->GetSize()), 0);
335
336  std::string pref_name;
337  if (!args->GetString(0, &pref_name))
338    return;
339
340  std::string metric;
341  if (args->GetSize() > 1)
342    args->GetString(1, &metric);
343
344  ClearPref(pref_name, metric);
345}
346
347void CoreOptionsHandler::HandleUserMetricsAction(const ListValue* args) {
348  std::string metric = UTF16ToUTF8(ExtractStringValue(args));
349  if (!metric.empty())
350    UserMetricsRecordAction(UserMetricsAction(metric.c_str()));
351}
352
353void CoreOptionsHandler::UpdateClearPluginLSOData() {
354  scoped_ptr<Value> enabled(
355      Value::CreateBooleanValue(clear_plugin_lso_data_enabled_.GetValue()));
356  web_ui_->CallJavascriptFunction(
357      "OptionsPage.setClearPluginLSODataEnabled", *enabled);
358}
359
360void CoreOptionsHandler::NotifyPrefChanged(const std::string* pref_name) {
361  if (*pref_name == prefs::kClearPluginLSODataEnabled) {
362    // This preference is stored in Local State, not in the user preferences.
363    UpdateClearPluginLSOData();
364    return;
365  }
366
367  PrefService* pref_service = web_ui_->GetProfile()->GetPrefs();
368  const PrefService::Preference* pref =
369      pref_service->FindPreference(pref_name->c_str());
370  if (pref) {
371    for (PreferenceCallbackMap::const_iterator iter =
372        pref_callback_map_.find(*pref_name);
373        iter != pref_callback_map_.end(); ++iter) {
374      const std::wstring& callback_function = iter->second;
375      ListValue result_value;
376      result_value.Append(Value::CreateStringValue(pref_name->c_str()));
377
378      DictionaryValue* dict = new DictionaryValue;
379      dict->Set("value", pref->GetValue()->DeepCopy());
380      dict->SetBoolean("managed", pref->IsManaged());
381      result_value.Append(dict);
382
383      web_ui_->CallJavascriptFunction(WideToASCII(callback_function),
384                                      result_value);
385    }
386  }
387}
388