Home | History | Annotate | Download | only in options
      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/ui/webui/options/core_options_handler.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/json/json_reader.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/strings/string16.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/chrome_notification_types.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/ui/options/options_util.h"
     19 #include "chrome/common/net/url_fixer_upper.h"
     20 #include "chrome/common/pref_names.h"
     21 #include "chrome/common/url_constants.h"
     22 #include "content/public/browser/notification_details.h"
     23 #include "content/public/browser/notification_types.h"
     24 #include "content/public/browser/user_metrics.h"
     25 #include "content/public/browser/web_ui.h"
     26 #include "grit/chromium_strings.h"
     27 #include "grit/generated_resources.h"
     28 #include "grit/locale_settings.h"
     29 #include "grit/theme_resources.h"
     30 #include "ui/base/l10n/l10n_util.h"
     31 #include "url/gurl.h"
     32 
     33 using content::UserMetricsAction;
     34 
     35 namespace options {
     36 
     37 namespace {
     38 
     39 // Only allow changes to the metrics reporting checkbox if we were succesfully
     40 // able to change the service.
     41 bool AllowMetricsReportingChange(const base::Value* to_value) {
     42   bool enable;
     43   if (!to_value->GetAsBoolean(&enable)) {
     44     NOTREACHED();
     45     return false;
     46   }
     47 
     48   return enable == OptionsUtil::ResolveMetricsReportingEnabled(enable);
     49 }
     50 
     51 }  // namespace
     52 
     53 CoreOptionsHandler::CoreOptionsHandler()
     54     : handlers_host_(NULL) {
     55 }
     56 
     57 CoreOptionsHandler::~CoreOptionsHandler() {}
     58 
     59 void CoreOptionsHandler::InitializeHandler() {
     60   Profile* profile = Profile::FromWebUI(web_ui());
     61 
     62   plugin_status_pref_setter_.Init(
     63       profile,
     64       base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
     65                  base::Unretained(this),
     66                  profile->GetPrefs()));
     67 
     68   pref_change_filters_[prefs::kMetricsReportingEnabled] =
     69       base::Bind(&AllowMetricsReportingChange);
     70 }
     71 
     72 void CoreOptionsHandler::InitializePage() {
     73   UpdateClearPluginLSOData();
     74   UpdatePepperFlashSettingsEnabled();
     75 }
     76 
     77 void CoreOptionsHandler::GetLocalizedValues(
     78     DictionaryValue* localized_strings) {
     79   GetStaticLocalizedValues(localized_strings);
     80 }
     81 
     82 void CoreOptionsHandler::GetStaticLocalizedValues(
     83     base::DictionaryValue* localized_strings) {
     84   DCHECK(localized_strings);
     85   // Main
     86   localized_strings->SetString("optionsPageTitle",
     87       l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
     88 
     89   // Controlled settings bubble.
     90   localized_strings->SetString("controlledSettingPolicy",
     91       l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
     92   localized_strings->SetString("controlledSettingExtension",
     93       l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
     94   localized_strings->SetString("controlledSettingRecommended",
     95       l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
     96   localized_strings->SetString("controlledSettingHasRecommendation",
     97       l10n_util::GetStringUTF16(
     98           IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
     99   localized_strings->SetString("controlledSettingFollowRecommendation",
    100       l10n_util::GetStringUTF16(
    101           IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
    102   localized_strings->SetString("controlledSettingsPolicy",
    103       l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
    104   localized_strings->SetString("controlledSettingsExtension",
    105       l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
    106 
    107   // Search
    108   RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
    109   localized_strings->SetString("searchPlaceholder",
    110       l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
    111   localized_strings->SetString("searchPageNoMatches",
    112       l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
    113   localized_strings->SetString("searchPageHelpLabel",
    114       l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
    115   localized_strings->SetString("searchPageHelpTitle",
    116       l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
    117           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
    118   localized_strings->SetString("searchPageHelpURL",
    119                                chrome::kSettingsSearchHelpURL);
    120 
    121   // Common
    122   localized_strings->SetString("ok",
    123       l10n_util::GetStringUTF16(IDS_OK));
    124   localized_strings->SetString("cancel",
    125       l10n_util::GetStringUTF16(IDS_CANCEL));
    126   localized_strings->SetString("learnMore",
    127       l10n_util::GetStringUTF16(IDS_LEARN_MORE));
    128   localized_strings->SetString("close",
    129       l10n_util::GetStringUTF16(IDS_CLOSE));
    130   localized_strings->SetString("done",
    131       l10n_util::GetStringUTF16(IDS_DONE));
    132 }
    133 
    134 void CoreOptionsHandler::Uninitialize() {
    135   std::string last_pref;
    136   for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
    137        iter != pref_callback_map_.end();
    138        ++iter) {
    139     if (last_pref != iter->first) {
    140       StopObservingPref(iter->first);
    141       last_pref = iter->first;
    142     }
    143   }
    144 }
    145 
    146 void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
    147                                              const std::string& pref_name) {
    148   if (pref_name == prefs::kClearPluginLSODataEnabled) {
    149     // This preference is stored in Local State, not in the user preferences.
    150     UpdateClearPluginLSOData();
    151     return;
    152   }
    153   if (pref_name == prefs::kPepperFlashSettingsEnabled) {
    154     UpdatePepperFlashSettingsEnabled();
    155     return;
    156   }
    157   NotifyPrefChanged(pref_name, std::string());
    158 }
    159 
    160 void CoreOptionsHandler::RegisterMessages() {
    161   registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
    162   local_state_registrar_.Init(g_browser_process->local_state());
    163 
    164   web_ui()->RegisterMessageCallback("coreOptionsInitialize",
    165       base::Bind(&CoreOptionsHandler::HandleInitialize,
    166                  base::Unretained(this)));
    167   web_ui()->RegisterMessageCallback("fetchPrefs",
    168       base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
    169                  base::Unretained(this)));
    170   web_ui()->RegisterMessageCallback("observePrefs",
    171       base::Bind(&CoreOptionsHandler::HandleObservePrefs,
    172                  base::Unretained(this)));
    173   web_ui()->RegisterMessageCallback("setBooleanPref",
    174       base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
    175                  base::Unretained(this)));
    176   web_ui()->RegisterMessageCallback("setIntegerPref",
    177       base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
    178                  base::Unretained(this)));
    179   web_ui()->RegisterMessageCallback("setDoublePref",
    180       base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
    181                  base::Unretained(this)));
    182   web_ui()->RegisterMessageCallback("setStringPref",
    183       base::Bind(&CoreOptionsHandler::HandleSetStringPref,
    184                  base::Unretained(this)));
    185   web_ui()->RegisterMessageCallback("setURLPref",
    186       base::Bind(&CoreOptionsHandler::HandleSetURLPref,
    187                  base::Unretained(this)));
    188   web_ui()->RegisterMessageCallback("setListPref",
    189       base::Bind(&CoreOptionsHandler::HandleSetListPref,
    190                  base::Unretained(this)));
    191   web_ui()->RegisterMessageCallback("clearPref",
    192       base::Bind(&CoreOptionsHandler::HandleClearPref,
    193                  base::Unretained(this)));
    194   web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
    195       base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
    196                  base::Unretained(this)));
    197 }
    198 
    199 void CoreOptionsHandler::HandleInitialize(const ListValue* args) {
    200   DCHECK(handlers_host_);
    201   handlers_host_->InitializeHandlers();
    202 }
    203 
    204 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
    205   return CreateValueForPref(pref_name, std::string());
    206 }
    207 
    208 void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
    209   if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) {
    210     local_state_registrar_.Add(
    211         pref_name.c_str(),
    212         base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
    213                    base::Unretained(this),
    214                    local_state_registrar_.prefs()));
    215   }
    216   // TODO(pneubeck): change this to if/else once kProxy is only used as a user
    217   // pref. Currently, it is both a user and a local state pref.
    218   if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(
    219           pref_name.c_str())) {
    220     registrar_.Add(
    221         pref_name.c_str(),
    222         base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
    223                    base::Unretained(this),
    224                    registrar_.prefs()));
    225   }
    226 }
    227 
    228 void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
    229   if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
    230     local_state_registrar_.Remove(pref_name.c_str());
    231   else
    232     registrar_.Remove(pref_name.c_str());
    233 }
    234 
    235 void CoreOptionsHandler::SetPref(const std::string& pref_name,
    236                                  const base::Value* value,
    237                                  const std::string& metric) {
    238   PrefService* pref_service = FindServiceForPref(pref_name);
    239   PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
    240   if (iter != pref_change_filters_.end()) {
    241     // Also check if the pref is user modifiable (don't even try to run the
    242     // filter function if the user is not allowed to change the pref).
    243     const PrefService::Preference* pref =
    244         pref_service->FindPreference(pref_name.c_str());
    245     if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
    246       // Reject the change; remind the page of the true value.
    247       NotifyPrefChanged(pref_name, std::string());
    248       return;
    249     }
    250   }
    251 
    252   switch (value->GetType()) {
    253     case base::Value::TYPE_BOOLEAN:
    254     case base::Value::TYPE_INTEGER:
    255     case base::Value::TYPE_DOUBLE:
    256     case base::Value::TYPE_STRING:
    257     case base::Value::TYPE_LIST:
    258       pref_service->Set(pref_name.c_str(), *value);
    259       break;
    260 
    261     default:
    262       NOTREACHED();
    263       return;
    264   }
    265 
    266   ProcessUserMetric(value, metric);
    267 }
    268 
    269 void CoreOptionsHandler::ClearPref(const std::string& pref_name,
    270                                    const std::string& metric) {
    271   PrefService* pref_service = FindServiceForPref(pref_name);
    272   pref_service->ClearPref(pref_name.c_str());
    273 
    274   if (!metric.empty())
    275     content::RecordComputedAction(metric);
    276 }
    277 
    278 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
    279                                            const std::string& metric) {
    280   if (metric.empty())
    281     return;
    282 
    283   std::string metric_string = metric;
    284   if (value->IsType(base::Value::TYPE_BOOLEAN)) {
    285     bool bool_value;
    286     CHECK(value->GetAsBoolean(&bool_value));
    287     metric_string += bool_value ? "_Enable" : "_Disable";
    288   }
    289 
    290   content::RecordComputedAction(metric_string);
    291 }
    292 
    293 void CoreOptionsHandler::NotifyPrefChanged(
    294     const std::string& pref_name,
    295     const std::string& controlling_pref_name) {
    296   scoped_ptr<base::Value> value(
    297       CreateValueForPref(pref_name, controlling_pref_name));
    298   DispatchPrefChangeNotification(pref_name, value.Pass());
    299 }
    300 
    301 void CoreOptionsHandler::DispatchPrefChangeNotification(
    302     const std::string& name,
    303     scoped_ptr<base::Value> value) {
    304   std::pair<PreferenceCallbackMap::const_iterator,
    305             PreferenceCallbackMap::const_iterator> range =
    306       pref_callback_map_.equal_range(name);
    307   ListValue result_value;
    308   result_value.Append(new base::StringValue(name.c_str()));
    309   result_value.Append(value.release());
    310   for (PreferenceCallbackMap::const_iterator iter = range.first;
    311        iter != range.second; ++iter) {
    312     const std::string& callback_function = iter->second;
    313     web_ui()->CallJavascriptFunction(callback_function, result_value);
    314   }
    315 }
    316 
    317 base::Value* CoreOptionsHandler::CreateValueForPref(
    318     const std::string& pref_name,
    319     const std::string& controlling_pref_name) {
    320   const PrefService* pref_service = FindServiceForPref(pref_name.c_str());
    321   const PrefService::Preference* pref =
    322       pref_service->FindPreference(pref_name.c_str());
    323   if (!pref) {
    324     NOTREACHED();
    325     return base::Value::CreateNullValue();
    326   }
    327   const PrefService::Preference* controlling_pref =
    328       pref_service->FindPreference(controlling_pref_name.c_str());
    329   if (!controlling_pref)
    330     controlling_pref = pref;
    331 
    332   DictionaryValue* dict = new DictionaryValue;
    333   dict->Set("value", pref->GetValue()->DeepCopy());
    334   if (controlling_pref->IsManaged())
    335     dict->SetString("controlledBy", "policy");
    336   else if (controlling_pref->IsExtensionControlled())
    337     dict->SetString("controlledBy", "extension");
    338   else if (controlling_pref->IsRecommended())
    339     dict->SetString("controlledBy", "recommended");
    340 
    341   const base::Value* recommended_value =
    342       controlling_pref->GetRecommendedValue();
    343   if (recommended_value)
    344     dict->Set("recommendedValue", recommended_value->DeepCopy());
    345   dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
    346   return dict;
    347 }
    348 
    349 PrefService* CoreOptionsHandler::FindServiceForPref(
    350     const std::string& pref_name) {
    351   // Proxy is a peculiar case: on ChromeOS, settings exist in both user
    352   // prefs and local state, but chrome://settings should affect only user prefs.
    353   // Elsewhere the proxy settings are stored in local state.
    354   // See http://crbug.com/157147
    355   PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
    356   if (pref_name == prefs::kProxy)
    357 #if defined(OS_CHROMEOS)
    358     return user_prefs;
    359 #else
    360     return g_browser_process->local_state();
    361 #endif
    362 
    363   // Find which PrefService contains the given pref. Pref names should not
    364   // be duplicated across services, however if they are, prefer the user's
    365   // prefs.
    366   if (user_prefs->FindPreference(pref_name.c_str()))
    367     return user_prefs;
    368 
    369   if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
    370     return g_browser_process->local_state();
    371 
    372   return user_prefs;
    373 }
    374 
    375 void CoreOptionsHandler::HandleFetchPrefs(const ListValue* args) {
    376   // First param is name of callback function, so, there needs to be at least
    377   // one more element for the actual preference identifier.
    378   DCHECK_GE(static_cast<int>(args->GetSize()), 2);
    379 
    380   // Get callback JS function name.
    381   const base::Value* callback;
    382   if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
    383     return;
    384 
    385   string16 callback_function;
    386   if (!callback->GetAsString(&callback_function))
    387     return;
    388 
    389   // Get the list of name for prefs to build the response dictionary.
    390   DictionaryValue result_value;
    391   const base::Value* list_member;
    392 
    393   for (size_t i = 1; i < args->GetSize(); i++) {
    394     if (!args->Get(i, &list_member))
    395       break;
    396 
    397     if (!list_member->IsType(base::Value::TYPE_STRING))
    398       continue;
    399 
    400     std::string pref_name;
    401     if (!list_member->GetAsString(&pref_name))
    402       continue;
    403 
    404     result_value.Set(pref_name.c_str(), FetchPref(pref_name));
    405   }
    406   web_ui()->CallJavascriptFunction(UTF16ToASCII(callback_function),
    407                                    result_value);
    408 }
    409 
    410 void CoreOptionsHandler::HandleObservePrefs(const ListValue* args) {
    411   // First param is name is JS callback function name, the rest are pref
    412   // identifiers that we are observing.
    413   DCHECK_GE(static_cast<int>(args->GetSize()), 2);
    414 
    415   // Get preference change callback function name.
    416   std::string callback_func_name;
    417   if (!args->GetString(0, &callback_func_name))
    418     return;
    419 
    420   // Get all other parameters - pref identifiers.
    421   for (size_t i = 1; i < args->GetSize(); i++) {
    422     const base::Value* list_member;
    423     if (!args->Get(i, &list_member))
    424       break;
    425 
    426     // Just ignore bad pref identifiers for now.
    427     std::string pref_name;
    428     if (!list_member->IsType(base::Value::TYPE_STRING) ||
    429         !list_member->GetAsString(&pref_name))
    430       continue;
    431 
    432     if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
    433       ObservePref(pref_name);
    434 
    435     pref_callback_map_.insert(
    436         PreferenceCallbackMap::value_type(pref_name, callback_func_name));
    437   }
    438 }
    439 
    440 void CoreOptionsHandler::HandleSetBooleanPref(const ListValue* args) {
    441   HandleSetPref(args, TYPE_BOOLEAN);
    442 }
    443 
    444 void CoreOptionsHandler::HandleSetIntegerPref(const ListValue* args) {
    445   HandleSetPref(args, TYPE_INTEGER);
    446 }
    447 
    448 void CoreOptionsHandler::HandleSetDoublePref(const ListValue* args) {
    449   HandleSetPref(args, TYPE_DOUBLE);
    450 }
    451 
    452 void CoreOptionsHandler::HandleSetStringPref(const ListValue* args) {
    453   HandleSetPref(args, TYPE_STRING);
    454 }
    455 
    456 void CoreOptionsHandler::HandleSetURLPref(const ListValue* args) {
    457   HandleSetPref(args, TYPE_URL);
    458 }
    459 
    460 void CoreOptionsHandler::HandleSetListPref(const ListValue* args) {
    461   HandleSetPref(args, TYPE_LIST);
    462 }
    463 
    464 void CoreOptionsHandler::HandleSetPref(const ListValue* args, PrefType type) {
    465   DCHECK_GT(static_cast<int>(args->GetSize()), 1);
    466 
    467   std::string pref_name;
    468   if (!args->GetString(0, &pref_name))
    469     return;
    470 
    471   const base::Value* value;
    472   if (!args->Get(1, &value))
    473     return;
    474 
    475   scoped_ptr<base::Value> temp_value;
    476 
    477   switch (type) {
    478     case TYPE_BOOLEAN:
    479       if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
    480         NOTREACHED();
    481         return;
    482       }
    483       break;
    484     case TYPE_INTEGER: {
    485       // In JS all numbers are doubles.
    486       double double_value;
    487       if (!value->GetAsDouble(&double_value)) {
    488         NOTREACHED();
    489         return;
    490       }
    491       int int_value = static_cast<int>(double_value);
    492       temp_value.reset(new base::FundamentalValue(int_value));
    493       value = temp_value.get();
    494       break;
    495     }
    496     case TYPE_DOUBLE:
    497       if (!value->IsType(base::Value::TYPE_DOUBLE)) {
    498         NOTREACHED();
    499         return;
    500       }
    501       break;
    502     case TYPE_STRING:
    503       if (!value->IsType(base::Value::TYPE_STRING)) {
    504         NOTREACHED();
    505         return;
    506       }
    507       break;
    508     case TYPE_URL: {
    509       std::string original;
    510       if (!value->GetAsString(&original)) {
    511         NOTREACHED();
    512         return;
    513       }
    514       GURL fixed = URLFixerUpper::FixupURL(original, std::string());
    515       temp_value.reset(new base::StringValue(fixed.spec()));
    516       value = temp_value.get();
    517       break;
    518     }
    519     case TYPE_LIST: {
    520       // In case we have a List pref we got a JSON string.
    521       std::string json_string;
    522       if (!value->GetAsString(&json_string)) {
    523         NOTREACHED();
    524         return;
    525       }
    526       temp_value.reset(
    527           base::JSONReader::Read(json_string));
    528       value = temp_value.get();
    529       if (!value->IsType(base::Value::TYPE_LIST)) {
    530         NOTREACHED();
    531         return;
    532       }
    533       break;
    534     }
    535     default:
    536       NOTREACHED();
    537   }
    538 
    539   std::string metric;
    540   if (args->GetSize() > 2 && !args->GetString(2, &metric))
    541     LOG(WARNING) << "Invalid metric parameter: " << pref_name;
    542   SetPref(pref_name, value, metric);
    543 }
    544 
    545 void CoreOptionsHandler::HandleClearPref(const ListValue* args) {
    546   DCHECK_GT(static_cast<int>(args->GetSize()), 0);
    547 
    548   std::string pref_name;
    549   if (!args->GetString(0, &pref_name))
    550     return;
    551 
    552   std::string metric;
    553   if (args->GetSize() > 1) {
    554     if (!args->GetString(1, &metric))
    555       NOTREACHED();
    556   }
    557 
    558   ClearPref(pref_name, metric);
    559 }
    560 
    561 void CoreOptionsHandler::HandleUserMetricsAction(const ListValue* args) {
    562   std::string metric = UTF16ToUTF8(ExtractStringValue(args));
    563   if (!metric.empty())
    564     content::RecordComputedAction(metric);
    565 }
    566 
    567 void CoreOptionsHandler::UpdateClearPluginLSOData() {
    568   base::FundamentalValue enabled(
    569           plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
    570   web_ui()->CallJavascriptFunction(
    571       "OptionsPage.setClearPluginLSODataEnabled", enabled);
    572 }
    573 
    574 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
    575   base::FundamentalValue enabled(
    576           plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
    577   web_ui()->CallJavascriptFunction(
    578       "OptionsPage.setPepperFlashSettingsEnabled", enabled);
    579 }
    580 
    581 }  // namespace options
    582