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/input_method/input_method_persistence.h"
6
7#include "base/logging.h"
8#include "base/prefs/pref_service.h"
9#include "base/prefs/scoped_user_pref_update.h"
10#include "base/sys_info.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/chromeos/input_method/input_method_util.h"
13#include "chrome/browser/chromeos/language_preferences.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/profiles/profile_manager.h"
16#include "chrome/common/pref_names.h"
17
18namespace chromeos {
19namespace input_method {
20namespace {
21
22void PersistSystemInputMethod(const std::string& input_method) {
23  if (!g_browser_process || !g_browser_process->local_state())
24    return;
25
26  g_browser_process->local_state()->SetString(
27        language_prefs::kPreferredKeyboardLayout, input_method);
28}
29
30static void SetUserLRUInputMethodPreference(const std::string& username,
31                                            const std::string& input_method,
32                                            PrefService* local_state) {
33  if (!username.empty() && !local_state->ReadOnly()) {
34    bool update_succeed = false;
35    {
36      // Updater may have side-effects, therefore we do not replace
37      // entry while updater exists.
38      DictionaryPrefUpdate updater(local_state, prefs::kUsersLRUInputMethod);
39      base::DictionaryValue* const users_lru_input_methods = updater.Get();
40      if (users_lru_input_methods) {
41        users_lru_input_methods->SetStringWithoutPathExpansion(username,
42                                                               input_method);
43        update_succeed = true;
44      }
45    }
46    if (!update_succeed) {
47      // Somehow key kUsersLRUInputMethod has value of invalid type.
48      // Replace and retry.
49      local_state->Set(prefs::kUsersLRUInputMethod, base::DictionaryValue());
50
51      DictionaryPrefUpdate updater(local_state, prefs::kUsersLRUInputMethod);
52      base::DictionaryValue* const users_lru_input_methods = updater.Get();
53      if (users_lru_input_methods) {
54        users_lru_input_methods->SetStringWithoutPathExpansion(username,
55                                                               input_method);
56        update_succeed = true;
57      }
58    }
59    if (!update_succeed) {
60      DVLOG(1) << "Failed to replace local_state.kUsersLRUInputMethod: '"
61               << prefs::kUsersLRUInputMethod << "' for '" << username << "'";
62    }
63  }
64}
65
66// Update user LRU keyboard layout for login screen
67static void SetUserLRUInputMethod(
68    const std::string& input_method,
69    const chromeos::input_method::InputMethodManager* const manager,
70    Profile* profile) {
71  // Skip if it's not a keyboard layout. Drop input methods including
72  // extension ones.
73  if (!manager->IsLoginKeyboard(input_method))
74    return;
75
76  if (profile == NULL)
77    return;
78
79  PrefService* const local_state = g_browser_process->local_state();
80
81  SetUserLRUInputMethodPreference(
82      profile->GetProfileName(), input_method, local_state);
83}
84
85void PersistUserInputMethod(const std::string& input_method,
86                            InputMethodManager* const manager) {
87  PrefService* user_prefs = NULL;
88  // Persist the method on a per user basis. Note that the keyboard settings are
89  // stored per user desktop and a visiting window will use the same input
90  // method as the desktop it is on (and not of the owner of the window).
91  Profile* profile = ProfileManager::GetActiveUserProfile();
92  if (profile)
93    user_prefs = profile->GetPrefs();
94  if (!user_prefs)
95    return;
96  SetUserLRUInputMethod(input_method, manager, profile);
97
98  const std::string current_input_method_on_pref =
99      user_prefs->GetString(prefs::kLanguageCurrentInputMethod);
100  if (current_input_method_on_pref == input_method)
101    return;
102
103  user_prefs->SetString(prefs::kLanguagePreviousInputMethod,
104                        current_input_method_on_pref);
105  user_prefs->SetString(prefs::kLanguageCurrentInputMethod,
106                        input_method);
107}
108
109}  // namespace
110
111InputMethodPersistence::InputMethodPersistence(
112    InputMethodManager* input_method_manager)
113    : input_method_manager_(input_method_manager),
114      ui_session_(InputMethodManager::STATE_LOGIN_SCREEN) {
115  input_method_manager_->AddObserver(this);
116}
117
118InputMethodPersistence::~InputMethodPersistence() {
119  input_method_manager_->RemoveObserver(this);
120}
121
122void InputMethodPersistence::InputMethodChanged(
123    InputMethodManager* manager, bool show_message) {
124  DCHECK_EQ(input_method_manager_, manager);
125  const std::string current_input_method =
126      manager->GetActiveIMEState()->GetCurrentInputMethod().id();
127  // Save the new input method id depending on the current browser state.
128  switch (ui_session_) {
129    case InputMethodManager::STATE_LOGIN_SCREEN:
130      if (!manager->IsLoginKeyboard(current_input_method)) {
131        DVLOG(1) << "Only keyboard layouts are supported: "
132                 << current_input_method;
133        return;
134      }
135      PersistSystemInputMethod(current_input_method);
136      return;
137    case InputMethodManager::STATE_BROWSER_SCREEN:
138      PersistUserInputMethod(current_input_method, manager);
139      return;
140    case InputMethodManager::STATE_LOCK_SCREEN:
141      // We use a special set of input methods on the screen. Do not update.
142      return;
143    case InputMethodManager::STATE_TERMINATING:
144      return;
145  }
146  NOTREACHED();
147}
148
149void InputMethodPersistence::OnSessionStateChange(
150    InputMethodManager::UISessionState new_ui_session) {
151  ui_session_ = new_ui_session;
152}
153
154void SetUserLRUInputMethodPreferenceForTesting(const std::string& username,
155                                               const std::string& input_method,
156                                               PrefService* const local_state) {
157  SetUserLRUInputMethodPreference(username, input_method, local_state);
158}
159
160}  // namespace input_method
161}  // namespace chromeos
162