1// Copyright 2013 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/chromeos/salsa_ui.h"
6
7#include "base/bind.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/prefs/pref_service.h"
10#include "base/values.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/common/pref_names.h"
14#include "chrome/common/url_constants.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/web_ui.h"
17#include "content/public/browser/web_ui_data_source.h"
18#include "grit/browser_resources.h"
19
20// Whitelist of which preferences are possible targets for Salsa treatments.
21// If new preferences are added and they are to be used in an experiment, then
22// they must be added to this list as well to keep chrome://salsa from
23// changing arbitrary values.
24
25namespace {
26
27const char* kWhitelist[] = {
28  prefs::kMaxSeparationForGestureTouchesInPixels,
29  prefs::kTabScrubActivationDelayInMS,
30  prefs::kOverscrollHorizontalThresholdComplete,
31  prefs::kOverscrollVerticalThresholdComplete,
32  prefs::kOverscrollMinimumThresholdStart,
33  prefs::kOverscrollVerticalThresholdStart,
34  prefs::kOverscrollHorizontalResistThreshold,
35  prefs::kOverscrollVerticalResistThreshold,
36};
37
38void RevertPreferences(PrefService* prefs,
39                       std::map<int, const base::Value*>* vals) {
40  std::map<int, const base::Value*>::const_iterator it;
41  for (it = vals->begin(); it != vals->end(); ++it) {
42    if (!prefs->FindPreference(kWhitelist[it->first]))
43      continue;
44
45    if (!it->second) {
46      prefs->ClearPref(kWhitelist[it->first]);
47    } else {
48      prefs->Set(kWhitelist[it->first], *it->second);
49      delete it->second;
50    }
51  }
52}
53
54} // namespace
55
56SalsaUI::SalsaUI(content::WebUI* web_ui)
57    : content::WebUIController(web_ui) {
58  // Set up the chrome://salsa source.
59  content::WebUIDataSource* html_source =
60      content::WebUIDataSource::Create(chrome::kChromeUISalsaHost);
61
62  // Register callback handlers.
63  web_ui->RegisterMessageCallback(
64      "salsaSetPreferenceValue",
65      base::Bind(&SalsaUI::SetPreferenceValue,
66                 base::Unretained(this)));
67  web_ui->RegisterMessageCallback(
68      "salsaBackupPreferenceValue",
69      base::Bind(&SalsaUI::BackupPreferenceValue,
70                 base::Unretained(this)));
71
72  // Add required resources.
73  html_source->AddResourcePath("salsa.css", IDR_SALSA_CSS);
74  html_source->AddResourcePath("salsa.js", IDR_SALSA_JS);
75  html_source->SetDefaultResource(IDR_SALSA_HTML);
76
77  Profile* profile = Profile::FromWebUI(web_ui);
78  content::WebUIDataSource::Add(profile, html_source);
79}
80
81SalsaUI::~SalsaUI() {
82  std::map<int, const base::Value*>* values_to_revert =
83      new std::map<int, const base::Value*>;
84  values_to_revert->swap(orig_values_);
85
86  Profile* profile = Profile::FromWebUI(web_ui());
87  PrefService* prefs = profile->GetPrefs();
88
89  content::BrowserThread::PostTask(
90      content::BrowserThread::UI,
91      FROM_HERE,
92      base::Bind(&RevertPreferences, prefs, base::Owned(values_to_revert))
93    );
94}
95
96int SalsaUI::WhitelistIndex(const char* key) const {
97  if (!key)
98    return -1;
99
100  int len = arraysize(kWhitelist);
101  for (int i = 0; i < len; ++i) {
102    if (!strcmp(key, kWhitelist[i]))
103      return i;
104  }
105  return -1;
106}
107
108void SalsaUI::SetPreferenceValue(const base::ListValue* args) {
109  std::string pref_name;
110  const base::Value* value;
111  if (!args->GetString(0, &pref_name) || !args->Get(1, &value))
112    return;
113
114  int index = WhitelistIndex(pref_name.c_str());
115  if (index < 0)
116    return;
117
118  Profile* profile = Profile::FromWebUI(web_ui());
119  PrefService* prefs = profile->GetPrefs();
120  const PrefService::Preference* pref =
121      prefs->FindPreference(kWhitelist[index]);
122
123  if (pref->GetType() == value->GetType()) {
124    prefs->Set(kWhitelist[index], *value);
125  } else if (pref->GetType() == base::Value::TYPE_DOUBLE &&
126             value->GetType() == base::Value::TYPE_INTEGER) {
127    int int_val;
128    if (!value->GetAsInteger(&int_val))
129      return;
130    base::FundamentalValue double_val(static_cast<double>(int_val));
131    prefs->Set(kWhitelist[index], double_val);
132  }
133}
134
135void SalsaUI::BackupPreferenceValue(const base::ListValue* args) {
136  std::string pref_name;
137  if (!args->GetString(0, &pref_name))
138    return;
139
140  int index = WhitelistIndex(pref_name.c_str());
141  if (index < 0)
142    return;
143
144  Profile* profile = Profile::FromWebUI(web_ui());
145  PrefService* prefs = profile->GetPrefs();
146  const PrefService::Preference* pref =
147      prefs->FindPreference(kWhitelist[index]);
148
149  if (!pref)
150    return;
151
152  // Get our own copy of the user defined value or NULL if they are using the
153  // default. You have to make a copy since they'll be used in the destructor
154  // to restore the values and we need to make sure they're still around.
155  orig_values_[index] =
156      pref->IsDefaultValue() ? NULL : pref->GetValue()->DeepCopy();
157}
158