Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / chromeos / system / input_device_settings.cc
blob2e6fbf47200a47dc3c1f42b61d799d0b9d98c790
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.
5 #include "chrome/browser/chromeos/system/input_device_settings.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/process/kill.h"
14 #include "base/process/launch.h"
15 #include "base/process/process_handle.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/sys_info.h"
19 #include "base/task_runner.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
23 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
24 #include "chrome/common/pref_names.h"
25 #include "chromeos/system/statistics_provider.h"
26 #include "content/public/browser/browser_thread.h"
28 namespace chromeos {
29 namespace system {
31 namespace {
33 InputDeviceSettings* g_instance_;
34 InputDeviceSettings* g_test_instance_;
36 const char kDeviceTypeTouchpad[] = "touchpad";
37 const char kDeviceTypeMouse[] = "mouse";
38 const char kInputControl[] = "/opt/google/input/inputcontrol";
40 typedef base::RefCountedData<bool> RefCountedBool;
42 bool ScriptExists(const std::string& script) {
43 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
44 return base::PathExists(base::FilePath(script));
47 // Executes the input control script asynchronously, if it exists.
48 void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) {
49 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
50 DCHECK(!argv.empty());
51 const std::string& script(argv[0]);
53 // Script must exist on device.
54 DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script));
56 if (!ScriptExists(script))
57 return;
59 base::ProcessHandle handle;
60 base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle);
61 base::EnsureProcessGetsReaped(handle);
64 void ExecuteScript(const std::vector<std::string>& argv) {
65 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
67 if (argv.size() == 1)
68 return;
70 VLOG(1) << "About to launch: \""
71 << CommandLine(argv).GetCommandLineString() << "\"";
73 // Control scripts can take long enough to cause SIGART during shutdown
74 // (http://crbug.com/261426). Run the blocking pool task with
75 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
76 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
77 scoped_refptr<base::TaskRunner> runner =
78 pool->GetTaskRunnerWithShutdownBehavior(
79 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
80 runner->PostTask(FROM_HERE, base::Bind(&ExecuteScriptOnFileThread, argv));
83 void AddSensitivityArguments(const char* device_type, int value,
84 std::vector<std::string>* argv) {
85 DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
86 argv->push_back(base::StringPrintf("--%s_sensitivity=%d",
87 device_type, value));
90 void AddTPControlArguments(const char* control,
91 bool enabled,
92 std::vector<std::string>* argv) {
93 argv->push_back(base::StringPrintf("--%s=%d", control, enabled ? 1 : 0));
96 void DeviceExistsBlockingPool(const char* device_type,
97 scoped_refptr<RefCountedBool> exists) {
98 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
99 exists->data = false;
100 if (!ScriptExists(kInputControl))
101 return;
103 std::vector<std::string> argv;
104 argv.push_back(kInputControl);
105 argv.push_back(base::StringPrintf("--type=%s", device_type));
106 argv.push_back("--list");
107 std::string output;
108 // Output is empty if the device is not found.
109 exists->data = base::GetAppOutput(CommandLine(argv), &output) &&
110 !output.empty();
111 DVLOG(1) << "DeviceExistsBlockingPool:" << device_type << "=" << exists->data;
114 void RunCallbackUIThread(
115 scoped_refptr<RefCountedBool> exists,
116 const InputDeviceSettings::DeviceExistsCallback& callback) {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
118 DVLOG(1) << "RunCallbackUIThread " << exists->data;
119 callback.Run(exists->data);
122 void DeviceExists(const char* script,
123 const InputDeviceSettings::DeviceExistsCallback& callback) {
124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
126 // One or both of the control scripts can apparently hang during shutdown
127 // (http://crbug.com/255546). Run the blocking pool task with
128 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
129 scoped_refptr<RefCountedBool> exists(new RefCountedBool(false));
130 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
131 scoped_refptr<base::TaskRunner> runner =
132 pool->GetTaskRunnerWithShutdownBehavior(
133 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
134 runner->PostTaskAndReply(FROM_HERE,
135 base::Bind(&DeviceExistsBlockingPool, script, exists),
136 base::Bind(&RunCallbackUIThread, exists, callback));
139 class InputDeviceSettingsImpl : public InputDeviceSettings {
140 public:
141 InputDeviceSettingsImpl();
143 private:
144 // Overridden from InputDeviceSettings.
145 virtual void TouchpadExists(const DeviceExistsCallback& callback) override;
146 virtual void UpdateTouchpadSettings(const TouchpadSettings& settings)
147 override;
148 virtual void SetTouchpadSensitivity(int value) override;
149 virtual void SetTapToClick(bool enabled) override;
150 virtual void SetThreeFingerClick(bool enabled) override;
151 virtual void SetTapDragging(bool enabled) override;
152 virtual void SetNaturalScroll(bool enabled) override;
153 virtual void MouseExists(const DeviceExistsCallback& callback) override;
154 virtual void UpdateMouseSettings(const MouseSettings& update) override;
155 virtual void SetMouseSensitivity(int value) override;
156 virtual void SetPrimaryButtonRight(bool right) override;
157 virtual bool ForceKeyboardDrivenUINavigation() override;
158 virtual void ReapplyTouchpadSettings() override;
159 virtual void ReapplyMouseSettings() override;
161 private:
162 TouchpadSettings current_touchpad_settings_;
163 MouseSettings current_mouse_settings_;
165 DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImpl);
168 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {}
170 void InputDeviceSettingsImpl::TouchpadExists(
171 const DeviceExistsCallback& callback) {
172 DeviceExists(kDeviceTypeTouchpad, callback);
175 void InputDeviceSettingsImpl::UpdateTouchpadSettings(
176 const TouchpadSettings& settings) {
177 std::vector<std::string> argv;
178 if (current_touchpad_settings_.Update(settings, &argv))
179 ExecuteScript(argv);
182 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value) {
183 TouchpadSettings settings;
184 settings.SetSensitivity(value);
185 UpdateTouchpadSettings(settings);
188 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled) {
189 TouchpadSettings settings;
190 settings.SetNaturalScroll(enabled);
191 UpdateTouchpadSettings(settings);
194 void InputDeviceSettingsImpl::SetTapToClick(bool enabled) {
195 TouchpadSettings settings;
196 settings.SetTapToClick(enabled);
197 UpdateTouchpadSettings(settings);
200 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled) {
201 // For Alex/ZGB.
202 TouchpadSettings settings;
203 settings.SetThreeFingerClick(enabled);
204 UpdateTouchpadSettings(settings);
207 void InputDeviceSettingsImpl::SetTapDragging(bool enabled) {
208 TouchpadSettings settings;
209 settings.SetTapDragging(enabled);
210 UpdateTouchpadSettings(settings);
213 void InputDeviceSettingsImpl::MouseExists(
214 const DeviceExistsCallback& callback) {
215 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
216 DeviceExists(kDeviceTypeMouse, callback);
219 void InputDeviceSettingsImpl::UpdateMouseSettings(const MouseSettings& update) {
220 std::vector<std::string> argv;
221 if (current_mouse_settings_.Update(update, &argv))
222 ExecuteScript(argv);
225 void InputDeviceSettingsImpl::SetMouseSensitivity(int value) {
226 MouseSettings settings;
227 settings.SetSensitivity(value);
228 UpdateMouseSettings(settings);
231 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right) {
232 MouseSettings settings;
233 settings.SetPrimaryButtonRight(right);
234 UpdateMouseSettings(settings);
237 bool InputDeviceSettingsImpl::ForceKeyboardDrivenUINavigation() {
238 policy::BrowserPolicyConnectorChromeOS* connector =
239 g_browser_process->platform_part()->browser_policy_connector_chromeos();
240 if (!connector)
241 return false;
243 policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
244 connector->GetDeviceCloudPolicyManager();
245 if (!policy_manager)
246 return false;
248 if (policy_manager->IsRemoraRequisition() ||
249 policy_manager->IsSharkRequisition()) {
250 return true;
253 bool keyboard_driven = false;
254 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag(
255 kOemKeyboardDrivenOobeKey, &keyboard_driven)) {
256 return keyboard_driven;
259 return false;
262 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() {
263 TouchpadSettings settings = current_touchpad_settings_;
264 current_touchpad_settings_ = TouchpadSettings();
265 UpdateTouchpadSettings(settings);
268 void InputDeviceSettingsImpl::ReapplyMouseSettings() {
269 MouseSettings settings = current_mouse_settings_;
270 current_mouse_settings_ = MouseSettings();
271 UpdateMouseSettings(settings);
274 } // namespace
276 TouchpadSettings::TouchpadSettings() {}
278 TouchpadSettings& TouchpadSettings::operator=(const TouchpadSettings& other) {
279 if (&other != this) {
280 sensitivity_ = other.sensitivity_;
281 tap_to_click_ = other.tap_to_click_;
282 three_finger_click_ = other.three_finger_click_;
283 tap_dragging_ = other.tap_dragging_;
284 natural_scroll_ = other.natural_scroll_;
286 return *this;
289 void TouchpadSettings::SetSensitivity(int value) {
290 sensitivity_.Set(value);
293 int TouchpadSettings::GetSensitivity() const {
294 return sensitivity_.value();
297 void TouchpadSettings::SetTapToClick(bool enabled) {
298 tap_to_click_.Set(enabled);
301 bool TouchpadSettings::GetTapToClick() const {
302 return tap_to_click_.value();
305 void TouchpadSettings::SetNaturalScroll(bool enabled) {
306 natural_scroll_.Set(enabled);
309 bool TouchpadSettings::GetNaturalScroll() const {
310 return natural_scroll_.value();
313 void TouchpadSettings::SetThreeFingerClick(bool enabled) {
314 three_finger_click_.Set(enabled);
317 bool TouchpadSettings::GetThreeFingerClick() const {
318 return three_finger_click_.value();
321 void TouchpadSettings::SetTapDragging(bool enabled) {
322 tap_dragging_.Set(enabled);
325 bool TouchpadSettings::GetTapDragging() const {
326 return tap_dragging_.value();
329 bool TouchpadSettings::Update(const TouchpadSettings& settings,
330 std::vector<std::string>* argv) {
331 if (argv)
332 argv->push_back(kInputControl);
333 bool updated = false;
334 if (sensitivity_.Update(settings.sensitivity_)) {
335 updated = true;
336 if (argv)
337 AddSensitivityArguments(kDeviceTypeTouchpad, sensitivity_.value(), argv);
339 if (tap_to_click_.Update(settings.tap_to_click_)) {
340 updated = true;
341 if (argv)
342 AddTPControlArguments("tapclick", tap_to_click_.value(), argv);
344 if (three_finger_click_.Update(settings.three_finger_click_)) {
345 updated = true;
346 if (argv)
347 AddTPControlArguments("t5r2_three_finger_click",
348 three_finger_click_.value(),
349 argv);
351 if (tap_dragging_.Update(settings.tap_dragging_)) {
352 updated = true;
353 if (argv)
354 AddTPControlArguments("tapdrag", tap_dragging_.value(), argv);
356 natural_scroll_.Update(settings.natural_scroll_);
357 // Always send natural scrolling to the shell command, as a workaround.
358 // See crbug.com/406480
359 if (natural_scroll_.is_set()) {
360 updated = true;
361 if (argv)
362 AddTPControlArguments("australian_scrolling", natural_scroll_.value(),
363 argv);
365 return updated;
368 MouseSettings::MouseSettings() {}
370 MouseSettings& MouseSettings::operator=(const MouseSettings& other) {
371 if (&other != this) {
372 sensitivity_ = other.sensitivity_;
373 primary_button_right_ = other.primary_button_right_;
375 return *this;
378 void MouseSettings::SetSensitivity(int value) {
379 sensitivity_.Set(value);
382 int MouseSettings::GetSensitivity() const {
383 return sensitivity_.value();
386 void MouseSettings::SetPrimaryButtonRight(bool right) {
387 primary_button_right_.Set(right);
390 bool MouseSettings::GetPrimaryButtonRight() const {
391 return primary_button_right_.value();
394 bool MouseSettings::Update(const MouseSettings& settings,
395 std::vector<std::string>* argv) {
396 if (argv)
397 argv->push_back(kInputControl);
398 bool updated = false;
399 if (sensitivity_.Update(settings.sensitivity_)) {
400 updated = true;
401 if (argv)
402 AddSensitivityArguments(kDeviceTypeMouse, sensitivity_.value(), argv);
404 if (primary_button_right_.Update(settings.primary_button_right_)) {
405 updated = true;
406 if (argv) {
407 AddTPControlArguments("mouse_swap_lr", primary_button_right_.value(),
408 argv);
411 return updated;
414 // static
415 InputDeviceSettings* InputDeviceSettings::Get() {
416 if (g_test_instance_)
417 return g_test_instance_;
418 if (!g_instance_)
419 g_instance_ = new InputDeviceSettingsImpl;
420 return g_instance_;
423 // static
424 void InputDeviceSettings::SetSettingsForTesting(
425 InputDeviceSettings* test_settings) {
426 if (g_test_instance_ == test_settings)
427 return;
428 delete g_test_instance_;
429 g_test_instance_ = test_settings;
432 } // namespace system
433 } // namespace chromeos