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/preferences.h"
6
7#include <vector>
8
9#include "ash/autoclick/autoclick_controller.h"
10#include "ash/magnifier/magnifier_constants.h"
11#include "ash/shell.h"
12#include "base/command_line.h"
13#include "base/i18n/time_formatting.h"
14#include "base/metrics/histogram.h"
15#include "base/prefs/pref_member.h"
16#include "base/prefs/pref_registry_simple.h"
17#include "base/prefs/scoped_user_pref_update.h"
18#include "base/strings/string_split.h"
19#include "base/strings/string_util.h"
20#include "base/strings/utf_string_conversions.h"
21#include "base/sys_info.h"
22#include "chrome/browser/browser_process.h"
23#include "chrome/browser/chrome_notification_types.h"
24#include "chrome/browser/chromeos/accessibility/magnification_manager.h"
25#include "chrome/browser/chromeos/drive/file_system_util.h"
26#include "chrome/browser/chromeos/input_method/input_method_util.h"
27#include "chrome/browser/chromeos/login/session/session_manager.h"
28#include "chrome/browser/chromeos/login/users/user.h"
29#include "chrome/browser/chromeos/system/input_device_settings.h"
30#include "chrome/browser/download/download_prefs.h"
31#include "chrome/browser/prefs/pref_service_syncable.h"
32#include "chrome/common/chrome_switches.h"
33#include "chrome/common/pref_names.h"
34#include "chromeos/chromeos_switches.h"
35#include "chromeos/ime/extension_ime_util.h"
36#include "chromeos/ime/ime_keyboard.h"
37#include "chromeos/ime/input_method_manager.h"
38#include "chromeos/system/statistics_provider.h"
39#include "components/feedback/tracing_manager.h"
40#include "components/pref_registry/pref_registry_syncable.h"
41#include "third_party/icu/source/i18n/unicode/timezone.h"
42#include "ui/events/event_constants.h"
43#include "ui/events/event_utils.h"
44#include "url/gurl.h"
45
46namespace chromeos {
47
48static const char kFallbackInputMethodLocale[] = "en-US";
49
50Preferences::Preferences()
51    : prefs_(NULL),
52      input_method_manager_(input_method::InputMethodManager::Get()),
53      user_(NULL),
54      user_is_primary_(false) {
55  // Do not observe shell, if there is no shell instance; e.g., in some unit
56  // tests.
57  if (ash::Shell::HasInstance())
58    ash::Shell::GetInstance()->AddShellObserver(this);
59}
60
61Preferences::Preferences(input_method::InputMethodManager* input_method_manager)
62    : prefs_(NULL),
63      input_method_manager_(input_method_manager),
64      user_(NULL),
65      user_is_primary_(false) {
66  // Do not observe shell, if there is no shell instance; e.g., in some unit
67  // tests.
68  if (ash::Shell::HasInstance())
69    ash::Shell::GetInstance()->AddShellObserver(this);
70}
71
72Preferences::~Preferences() {
73  prefs_->RemoveObserver(this);
74  UserManager::Get()->RemoveSessionStateObserver(this);
75  // If shell instance is destoryed before this preferences instance, there is
76  // no need to remove this shell observer.
77  if (ash::Shell::HasInstance())
78    ash::Shell::GetInstance()->RemoveShellObserver(this);
79}
80
81// static
82void Preferences::RegisterPrefs(PrefRegistrySimple* registry) {
83  registry->RegisterBooleanPref(prefs::kOwnerPrimaryMouseButtonRight, false);
84  registry->RegisterBooleanPref(prefs::kOwnerTapToClickEnabled, true);
85  registry->RegisterBooleanPref(prefs::kAccessibilityVirtualKeyboardEnabled,
86                                false);
87  registry->RegisterStringPref(prefs::kLogoutStartedLast, std::string());
88}
89
90// static
91void Preferences::RegisterProfilePrefs(
92    user_prefs::PrefRegistrySyncable* registry) {
93  std::string hardware_keyboard_id;
94  // TODO(yusukes): Remove the runtime hack.
95  if (base::SysInfo::IsRunningOnChromeOS()) {
96    DCHECK(g_browser_process);
97    PrefService* local_state = g_browser_process->local_state();
98    DCHECK(local_state);
99    hardware_keyboard_id =
100        local_state->GetString(prefs::kHardwareKeyboardLayout);
101  } else {
102    hardware_keyboard_id = "xkb:us::eng";  // only for testing.
103  }
104
105  registry->RegisterBooleanPref(
106      prefs::kPerformanceTracingEnabled,
107      false,
108      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
109
110  registry->RegisterBooleanPref(
111      prefs::kTapToClickEnabled,
112      true,
113      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
114  registry->RegisterBooleanPref(
115      prefs::kTapDraggingEnabled,
116      false,
117      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
118  registry->RegisterBooleanPref(
119      prefs::kEnableTouchpadThreeFingerClick,
120      false,
121      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
122  registry->RegisterBooleanPref(
123      prefs::kNaturalScroll,
124      CommandLine::ForCurrentProcess()->HasSwitch(
125          switches::kNaturalScrollDefault),
126      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
127  registry->RegisterBooleanPref(
128      prefs::kPrimaryMouseButtonRight,
129      false,
130      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
131  registry->RegisterBooleanPref(
132      prefs::kLabsMediaplayerEnabled,
133      false,
134      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
135  registry->RegisterBooleanPref(
136      prefs::kLabsAdvancedFilesystemEnabled,
137      false,
138      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
139  registry->RegisterBooleanPref(
140      prefs::kAccessibilityStickyKeysEnabled,
141      false,
142      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
143  registry->RegisterBooleanPref(
144      prefs::kAccessibilityLargeCursorEnabled,
145      false,
146      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
147  registry->RegisterBooleanPref(
148      prefs::kAccessibilitySpokenFeedbackEnabled,
149      false,
150      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
151  registry->RegisterBooleanPref(
152      prefs::kAccessibilityHighContrastEnabled,
153      false,
154      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
155  registry->RegisterBooleanPref(
156      prefs::kAccessibilityScreenMagnifierEnabled,
157      false,
158      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
159  registry->RegisterIntegerPref(
160      prefs::kAccessibilityScreenMagnifierType,
161      ash::kDefaultMagnifierType,
162      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
163  registry->RegisterDoublePref(
164      prefs::kAccessibilityScreenMagnifierScale,
165      std::numeric_limits<double>::min(),
166      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
167  registry->RegisterBooleanPref(
168      prefs::kAccessibilityAutoclickEnabled,
169      false,
170      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
171  registry->RegisterIntegerPref(
172      prefs::kAccessibilityAutoclickDelayMs,
173      ash::AutoclickController::kDefaultAutoclickDelayMs,
174      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
175  registry->RegisterBooleanPref(
176      prefs::kAccessibilityVirtualKeyboardEnabled,
177      false,
178      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
179  registry->RegisterBooleanPref(
180      prefs::kShouldAlwaysShowAccessibilityMenu,
181      false,
182      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
183  registry->RegisterIntegerPref(
184      prefs::kMouseSensitivity,
185      3,
186      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
187  registry->RegisterIntegerPref(
188      prefs::kTouchpadSensitivity,
189      3,
190      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
191  registry->RegisterBooleanPref(
192      prefs::kUse24HourClock,
193      base::GetHourClockType() == base::k24HourClock,
194      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
195  registry->RegisterBooleanPref(
196      prefs::kDisableDrive,
197      false,
198      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
199  registry->RegisterBooleanPref(
200      prefs::kDisableDriveOverCellular,
201      true,
202      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
203  registry->RegisterBooleanPref(
204      prefs::kDisableDriveHostedFiles,
205      false,
206      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
207  // We don't sync prefs::kLanguageCurrentInputMethod and PreviousInputMethod
208  // because they're just used to track the logout state of the device.
209  registry->RegisterStringPref(
210      prefs::kLanguageCurrentInputMethod,
211      "",
212      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
213  registry->RegisterStringPref(
214      prefs::kLanguagePreviousInputMethod,
215      "",
216      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
217  // We don't sync the list of input methods and preferred languages since a
218  // user might use two or more devices with different hardware keyboards.
219  // crosbug.com/15181
220  registry->RegisterStringPref(
221      prefs::kLanguagePreferredLanguages,
222      kFallbackInputMethodLocale,
223      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
224  registry->RegisterStringPref(
225      prefs::kLanguagePreloadEngines,
226      hardware_keyboard_id,
227      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
228  registry->RegisterStringPref(
229      prefs::kLanguageEnabledExtensionImes,
230      "",
231      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
232
233  registry->RegisterIntegerPref(
234      prefs::kLanguageRemapSearchKeyTo,
235      input_method::kSearchKey,
236      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
237  registry->RegisterIntegerPref(
238      prefs::kLanguageRemapControlKeyTo,
239      input_method::kControlKey,
240      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
241  registry->RegisterIntegerPref(
242      prefs::kLanguageRemapAltKeyTo,
243      input_method::kAltKey,
244      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
245  // We don't sync the CapsLock remapping pref, since the UI hides this pref
246  // on certain devices, so syncing a non-default value to a device that
247  // doesn't allow changing the pref would be odd. http://crbug.com/167237
248  registry->RegisterIntegerPref(
249      prefs::kLanguageRemapCapsLockKeyTo,
250      input_method::kCapsLockKey,
251      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
252  registry->RegisterIntegerPref(
253      prefs::kLanguageRemapDiamondKeyTo,
254      input_method::kControlKey,
255      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
256  // The following pref isn't synced since the user may desire a different value
257  // depending on whether an external keyboard is attached to a particular
258  // device.
259  registry->RegisterBooleanPref(
260      prefs::kLanguageSendFunctionKeys,
261      false,
262      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
263  // We don't sync the following keyboard prefs since they are not user-
264  // configurable.
265  registry->RegisterBooleanPref(
266      prefs::kLanguageXkbAutoRepeatEnabled,
267      true,
268      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
269  registry->RegisterIntegerPref(
270      prefs::kLanguageXkbAutoRepeatDelay,
271      language_prefs::kXkbAutoRepeatDelayInMs,
272      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
273  registry->RegisterIntegerPref(
274      prefs::kLanguageXkbAutoRepeatInterval,
275      language_prefs::kXkbAutoRepeatIntervalInMs,
276      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
277
278  // Mobile plan notifications default to on.
279  registry->RegisterBooleanPref(
280      prefs::kShowPlanNotifications,
281      true,
282      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
283
284  // 3G first-time usage promo will be shown at least once.
285  registry->RegisterBooleanPref(
286      prefs::kShow3gPromoNotification,
287      true,
288      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
289
290  // Initially all existing users would see "What's new" for current version
291  // after update.
292  registry->RegisterStringPref(prefs::kChromeOSReleaseNotesVersion,
293                               "0.0.0.0",
294                               user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
295
296  registry->RegisterBooleanPref(
297      prefs::kExternalStorageDisabled,
298      false,
299      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
300
301  registry->RegisterStringPref(
302      prefs::kTermsOfServiceURL,
303      "",
304      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
305
306  registry->RegisterBooleanPref(
307      prefs::kTouchHudProjectionEnabled,
308      false,
309      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
310
311  registry->RegisterBooleanPref(
312      prefs::kTouchVirtualKeyboardEnabled,
313      false,
314      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
315}
316
317void Preferences::InitUserPrefs(PrefServiceSyncable* prefs) {
318  prefs_ = prefs;
319
320  BooleanPrefMember::NamedChangeCallback callback =
321      base::Bind(&Preferences::OnPreferenceChanged, base::Unretained(this));
322
323  performance_tracing_enabled_.Init(prefs::kPerformanceTracingEnabled,
324                                    prefs, callback);
325  tap_to_click_enabled_.Init(prefs::kTapToClickEnabled, prefs, callback);
326  tap_dragging_enabled_.Init(prefs::kTapDraggingEnabled, prefs, callback);
327  three_finger_click_enabled_.Init(prefs::kEnableTouchpadThreeFingerClick,
328      prefs, callback);
329  natural_scroll_.Init(prefs::kNaturalScroll, prefs, callback);
330  mouse_sensitivity_.Init(prefs::kMouseSensitivity, prefs, callback);
331  touchpad_sensitivity_.Init(prefs::kTouchpadSensitivity, prefs, callback);
332  primary_mouse_button_right_.Init(prefs::kPrimaryMouseButtonRight,
333                                   prefs, callback);
334  download_default_directory_.Init(prefs::kDownloadDefaultDirectory,
335                                   prefs, callback);
336  touch_hud_projection_enabled_.Init(prefs::kTouchHudProjectionEnabled,
337                                     prefs, callback);
338  preload_engines_.Init(prefs::kLanguagePreloadEngines, prefs, callback);
339  enabled_extension_imes_.Init(prefs::kLanguageEnabledExtensionImes,
340                               prefs, callback);
341  current_input_method_.Init(prefs::kLanguageCurrentInputMethod,
342                             prefs, callback);
343  previous_input_method_.Init(prefs::kLanguagePreviousInputMethod,
344                              prefs, callback);
345
346  xkb_auto_repeat_enabled_.Init(
347      prefs::kLanguageXkbAutoRepeatEnabled, prefs, callback);
348  xkb_auto_repeat_delay_pref_.Init(
349      prefs::kLanguageXkbAutoRepeatDelay, prefs, callback);
350  xkb_auto_repeat_interval_pref_.Init(
351      prefs::kLanguageXkbAutoRepeatInterval, prefs, callback);
352}
353
354void Preferences::Init(PrefServiceSyncable* prefs, const User* user) {
355  DCHECK(user);
356  user_ = user;
357  user_is_primary_ = UserManager::Get()->GetPrimaryUser() == user_;
358  InitUserPrefs(prefs);
359
360  UserManager::Get()->AddSessionStateObserver(this);
361
362  // This causes OnIsSyncingChanged to be called when the value of
363  // PrefService::IsSyncing() changes.
364  prefs->AddObserver(this);
365
366  // Initialize preferences to currently saved state.
367  ApplyPreferences(REASON_INITIALIZATION, "");
368
369  // If a guest is logged in, initialize the prefs as if this is the first
370  // login. For a regular user this is done in
371  // SessionManager::InitProfilePreferences().
372  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession))
373    SessionManager::SetFirstLoginPrefs(prefs);
374}
375
376void Preferences::InitUserPrefsForTesting(PrefServiceSyncable* prefs,
377                                          const User* user) {
378  user_ = user;
379  InitUserPrefs(prefs);
380}
381
382void Preferences::SetInputMethodListForTesting() {
383  SetInputMethodList();
384}
385
386void Preferences::OnPreferenceChanged(const std::string& pref_name) {
387  ApplyPreferences(REASON_PREF_CHANGED, pref_name);
388}
389
390void Preferences::ApplyPreferences(ApplyReason reason,
391                                   const std::string& pref_name) {
392  DCHECK(reason != REASON_PREF_CHANGED || !pref_name.empty());
393  const bool user_is_owner =
394      UserManager::Get()->GetOwnerEmail() == user_->email();
395  const bool user_is_active = user_->is_active();
396
397  system::TouchpadSettings touchpad_settings;
398  system::MouseSettings mouse_settings;
399
400  if (user_is_primary_ && (reason == REASON_INITIALIZATION ||
401                           pref_name == prefs::kPerformanceTracingEnabled)) {
402    const bool enabled = performance_tracing_enabled_.GetValue();
403    if (enabled)
404      tracing_manager_ = TracingManager::Create();
405    else
406      tracing_manager_.reset();
407  }
408  if (reason != REASON_PREF_CHANGED || pref_name == prefs::kTapToClickEnabled) {
409    const bool enabled = tap_to_click_enabled_.GetValue();
410    if (user_is_active)
411      touchpad_settings.SetTapToClick(enabled);
412    if (reason == REASON_PREF_CHANGED)
413      UMA_HISTOGRAM_BOOLEAN("Touchpad.TapToClick.Changed", enabled);
414    else if (reason == REASON_INITIALIZATION)
415      UMA_HISTOGRAM_BOOLEAN("Touchpad.TapToClick.Started", enabled);
416
417    // Save owner preference in local state to use on login screen.
418    if (user_is_owner) {
419      PrefService* prefs = g_browser_process->local_state();
420      if (prefs->GetBoolean(prefs::kOwnerTapToClickEnabled) != enabled)
421        prefs->SetBoolean(prefs::kOwnerTapToClickEnabled, enabled);
422    }
423  }
424  if (reason != REASON_PREF_CHANGED ||
425      pref_name == prefs::kTapDraggingEnabled) {
426    const bool enabled = tap_dragging_enabled_.GetValue();
427    if (user_is_active)
428      touchpad_settings.SetTapDragging(enabled);
429    if (reason == REASON_PREF_CHANGED)
430      UMA_HISTOGRAM_BOOLEAN("Touchpad.TapDragging.Changed", enabled);
431    else if (reason == REASON_INITIALIZATION)
432      UMA_HISTOGRAM_BOOLEAN("Touchpad.TapDragging.Started", enabled);
433  }
434  if (reason != REASON_PREF_CHANGED ||
435      pref_name == prefs::kEnableTouchpadThreeFingerClick) {
436    const bool enabled = three_finger_click_enabled_.GetValue();
437    if (user_is_active)
438      touchpad_settings.SetThreeFingerClick(enabled);
439    if (reason == REASON_PREF_CHANGED)
440      UMA_HISTOGRAM_BOOLEAN("Touchpad.ThreeFingerClick.Changed", enabled);
441    else if (reason == REASON_INITIALIZATION)
442      UMA_HISTOGRAM_BOOLEAN("Touchpad.ThreeFingerClick.Started", enabled);
443  }
444  if (reason != REASON_PREF_CHANGED || pref_name == prefs::kNaturalScroll) {
445    // Force natural scroll default if we've sync'd and if the cmd line arg is
446    // set.
447    ForceNaturalScrollDefault();
448
449    const bool enabled = natural_scroll_.GetValue();
450    DVLOG(1) << "Natural scroll set to " << enabled;
451    if (user_is_active)
452      touchpad_settings.SetNaturalScroll(enabled);
453    if (reason == REASON_PREF_CHANGED)
454      UMA_HISTOGRAM_BOOLEAN("Touchpad.NaturalScroll.Changed", enabled);
455    else if (reason == REASON_INITIALIZATION)
456      UMA_HISTOGRAM_BOOLEAN("Touchpad.NaturalScroll.Started", enabled);
457  }
458  if (reason != REASON_PREF_CHANGED || pref_name == prefs::kMouseSensitivity) {
459    const int sensitivity = mouse_sensitivity_.GetValue();
460    if (user_is_active)
461      mouse_settings.SetSensitivity(sensitivity);
462    if (reason == REASON_PREF_CHANGED) {
463      UMA_HISTOGRAM_ENUMERATION("Mouse.PointerSensitivity.Changed",
464                                sensitivity,
465                                system::kMaxPointerSensitivity + 1);
466    } else if (reason == REASON_INITIALIZATION) {
467      UMA_HISTOGRAM_ENUMERATION("Mouse.PointerSensitivity.Started",
468                                sensitivity,
469                                system::kMaxPointerSensitivity + 1);
470    }
471  }
472  if (reason != REASON_PREF_CHANGED ||
473      pref_name == prefs::kTouchpadSensitivity) {
474    const int sensitivity = touchpad_sensitivity_.GetValue();
475    if (user_is_active)
476      touchpad_settings.SetSensitivity(sensitivity);
477    if (reason == REASON_PREF_CHANGED) {
478      UMA_HISTOGRAM_ENUMERATION("Touchpad.PointerSensitivity.Changed",
479                                sensitivity,
480                                system::kMaxPointerSensitivity + 1);
481    } else if (reason == REASON_INITIALIZATION) {
482      UMA_HISTOGRAM_ENUMERATION("Touchpad.PointerSensitivity.Started",
483                                sensitivity,
484                                system::kMaxPointerSensitivity + 1);
485    }
486  }
487  if (reason != REASON_PREF_CHANGED ||
488      pref_name == prefs::kPrimaryMouseButtonRight) {
489    const bool right = primary_mouse_button_right_.GetValue();
490    if (user_is_active)
491      mouse_settings.SetPrimaryButtonRight(right);
492    if (reason == REASON_PREF_CHANGED)
493      UMA_HISTOGRAM_BOOLEAN("Mouse.PrimaryButtonRight.Changed", right);
494    else if (reason == REASON_INITIALIZATION)
495      UMA_HISTOGRAM_BOOLEAN("Mouse.PrimaryButtonRight.Started", right);
496    // Save owner preference in local state to use on login screen.
497    if (user_is_owner) {
498      PrefService* prefs = g_browser_process->local_state();
499      if (prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight) != right)
500        prefs->SetBoolean(prefs::kOwnerPrimaryMouseButtonRight, right);
501    }
502  }
503  if (reason != REASON_PREF_CHANGED ||
504      pref_name == prefs::kDownloadDefaultDirectory) {
505    const bool default_download_to_drive = drive::util::IsUnderDriveMountPoint(
506        download_default_directory_.GetValue());
507    if (reason == REASON_PREF_CHANGED)
508      UMA_HISTOGRAM_BOOLEAN(
509          "FileBrowser.DownloadDestination.IsGoogleDrive.Changed",
510          default_download_to_drive);
511    else if (reason == REASON_INITIALIZATION)
512      UMA_HISTOGRAM_BOOLEAN(
513          "FileBrowser.DownloadDestination.IsGoogleDrive.Started",
514          default_download_to_drive);
515  }
516  if (reason != REASON_PREF_CHANGED ||
517      pref_name == prefs::kTouchHudProjectionEnabled) {
518    if (user_is_active) {
519      const bool enabled = touch_hud_projection_enabled_.GetValue();
520      ash::Shell::GetInstance()->SetTouchHudProjectionEnabled(enabled);
521    }
522  }
523
524  if (reason != REASON_PREF_CHANGED ||
525      pref_name == prefs::kLanguageXkbAutoRepeatEnabled) {
526    if (user_is_active) {
527      const bool enabled = xkb_auto_repeat_enabled_.GetValue();
528      input_method::InputMethodManager::Get()
529          ->GetImeKeyboard()
530          ->SetAutoRepeatEnabled(enabled);
531    }
532  }
533  if (reason != REASON_PREF_CHANGED ||
534      pref_name == prefs::kLanguageXkbAutoRepeatDelay ||
535      pref_name == prefs::kLanguageXkbAutoRepeatInterval) {
536    if (user_is_active)
537      UpdateAutoRepeatRate();
538  }
539
540  if (reason != REASON_PREF_CHANGED && user_is_active) {
541    SetInputMethodList();
542  } else if (pref_name == prefs::kLanguagePreloadEngines && user_is_active) {
543    SetLanguageConfigStringListAsCSV(language_prefs::kGeneralSectionName,
544                                     language_prefs::kPreloadEnginesConfigName,
545                                     preload_engines_.GetValue());
546  }
547
548  if (reason != REASON_PREF_CHANGED ||
549      pref_name == prefs::kLanguageEnabledExtensionImes) {
550    if (user_is_active) {
551      std::string value(enabled_extension_imes_.GetValue());
552
553      std::vector<std::string> split_values;
554      if (!value.empty())
555        base::SplitString(value, ',', &split_values);
556
557      input_method_manager_->SetEnabledExtensionImes(&split_values);
558    }
559  }
560
561  if (user_is_active) {
562    system::InputDeviceSettings::Get()->UpdateTouchpadSettings(
563        touchpad_settings);
564    system::InputDeviceSettings::Get()->UpdateMouseSettings(mouse_settings);
565  }
566}
567
568void Preferences::OnIsSyncingChanged() {
569  DVLOG(1) << "OnIsSyncingChanged";
570  ForceNaturalScrollDefault();
571}
572
573void Preferences::ForceNaturalScrollDefault() {
574  DVLOG(1) << "ForceNaturalScrollDefault";
575  if (CommandLine::ForCurrentProcess()->HasSwitch(
576          switches::kNaturalScrollDefault) &&
577      prefs_->IsSyncing() &&
578      !prefs_->GetUserPrefValue(prefs::kNaturalScroll)) {
579    DVLOG(1) << "Natural scroll forced to true";
580    natural_scroll_.SetValue(true);
581    UMA_HISTOGRAM_BOOLEAN("Touchpad.NaturalScroll.Forced", true);
582  }
583}
584
585void Preferences::SetLanguageConfigStringListAsCSV(const char* section,
586                                                   const char* name,
587                                                   const std::string& value) {
588  VLOG(1) << "Setting " << name << " to '" << value << "'";
589
590  std::vector<std::string> split_values;
591  if (!value.empty())
592    base::SplitString(value, ',', &split_values);
593
594  // Transfers the xkb id to extension-xkb id.
595  if (input_method_manager_->MigrateInputMethods(&split_values))
596    preload_engines_.SetValue(JoinString(split_values, ','));
597
598  if (section == std::string(language_prefs::kGeneralSectionName) &&
599      name == std::string(language_prefs::kPreloadEnginesConfigName)) {
600    input_method_manager_->ReplaceEnabledInputMethods(split_values);
601    return;
602  }
603}
604
605void Preferences::SetInputMethodList() {
606  // When |preload_engines_| are set, InputMethodManager::ChangeInputMethod()
607  // might be called to change the current input method to the first one in the
608  // |preload_engines_| list. This also updates previous/current input method
609  // prefs. That's why GetValue() calls are placed before the
610  // SetLanguageConfigStringListAsCSV() call below.
611  const std::string previous_input_method_id =
612      previous_input_method_.GetValue();
613  const std::string current_input_method_id = current_input_method_.GetValue();
614  SetLanguageConfigStringListAsCSV(language_prefs::kGeneralSectionName,
615                                   language_prefs::kPreloadEnginesConfigName,
616                                   preload_engines_.GetValue());
617
618  // ChangeInputMethod() has to be called AFTER the value of |preload_engines_|
619  // is sent to the InputMethodManager. Otherwise, the ChangeInputMethod request
620  // might be ignored as an invalid input method ID. The ChangeInputMethod()
621  // calls are also necessary to restore the previous/current input method prefs
622  // which could have been modified by the SetLanguageConfigStringListAsCSV call
623  // above to the original state.
624  if (!previous_input_method_id.empty())
625    input_method_manager_->ChangeInputMethod(previous_input_method_id);
626  if (!current_input_method_id.empty())
627    input_method_manager_->ChangeInputMethod(current_input_method_id);
628}
629
630void Preferences::UpdateAutoRepeatRate() {
631  input_method::AutoRepeatRate rate;
632  rate.initial_delay_in_ms = xkb_auto_repeat_delay_pref_.GetValue();
633  rate.repeat_interval_in_ms = xkb_auto_repeat_interval_pref_.GetValue();
634  DCHECK(rate.initial_delay_in_ms > 0);
635  DCHECK(rate.repeat_interval_in_ms > 0);
636  input_method::InputMethodManager::Get()
637      ->GetImeKeyboard()
638      ->SetAutoRepeatRate(rate);
639}
640
641void Preferences::OnTouchHudProjectionToggled(bool enabled) {
642  if (touch_hud_projection_enabled_.GetValue() == enabled)
643    return;
644  if (!user_->is_active())
645    return;
646  touch_hud_projection_enabled_.SetValue(enabled);
647}
648
649void Preferences::ActiveUserChanged(const User* active_user) {
650  if (active_user != user_)
651    return;
652  ApplyPreferences(REASON_ACTIVE_USER_CHANGED, "");
653}
654
655}  // namespace chromeos
656