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