summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos/input_method/browser_state_monitor.cc
blob: 0b2a8a273b01f4b9a62440b13d313fb8669bb230 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/input_method/browser_state_monitor.h"

#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/language_preferences.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"

namespace chromeos {
namespace input_method {
namespace {

PrefService* GetPrefService() {
  Profile* profile = ProfileManager::GetDefaultProfile();
  if (profile)
    return profile->GetPrefs();
  return NULL;
}

}  // namespace

BrowserStateMonitor::BrowserStateMonitor(InputMethodManager* manager)
    : manager_(manager),
      state_(InputMethodManager::STATE_LOGIN_SCREEN),
      pref_service_(NULL) {
  notification_registrar_.Add(this,
                              chrome::NOTIFICATION_LOGIN_USER_CHANGED,
                              content::NotificationService::AllSources());
  notification_registrar_.Add(this,
                              chrome::NOTIFICATION_SESSION_STARTED,
                              content::NotificationService::AllSources());
  notification_registrar_.Add(this,
                              chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
                              content::NotificationService::AllSources());
  // We should not use ALL_BROWSERS_CLOSING here since logout might be cancelled
  // by JavaScript after ALL_BROWSERS_CLOSING is sent (crosbug.com/11055).
  notification_registrar_.Add(this,
                              chrome::NOTIFICATION_APP_TERMINATING,
                              content::NotificationService::AllSources());

  manager_->SetState(state_);
  manager_->AddObserver(this);
}

BrowserStateMonitor::~BrowserStateMonitor() {
  manager_->RemoveObserver(this);
}

void BrowserStateMonitor::SetPrefServiceForTesting(PrefService* pref_service) {
  pref_service_ = pref_service;
}

void BrowserStateMonitor::UpdateLocalState(
    const std::string& current_input_method) {
  if (!g_browser_process || !g_browser_process->local_state())
    return;
  g_browser_process->local_state()->SetString(
      language_prefs::kPreferredKeyboardLayout,
      current_input_method);
}

void BrowserStateMonitor::UpdateUserPreferences(
    const std::string& current_input_method) {
  PrefService* pref_service = pref_service_ ? pref_service_ : GetPrefService();
  DCHECK(pref_service);

  // Even though we're DCHECK'ing to catch this on debug builds, we don't
  // want to crash a release build in case the pref service is no longer
  // available.
  if (!pref_service)
    return;

  const std::string current_input_method_on_pref =
      pref_service->GetString(prefs::kLanguageCurrentInputMethod);
  if (current_input_method_on_pref == current_input_method)
    return;

  pref_service->SetString(prefs::kLanguagePreviousInputMethod,
                          current_input_method_on_pref);
  pref_service->SetString(prefs::kLanguageCurrentInputMethod,
                          current_input_method);
}

void BrowserStateMonitor::InputMethodChanged(InputMethodManager* manager,
                                             bool show_message) {
  DCHECK_EQ(manager_, manager);
  const std::string current_input_method =
      manager->GetCurrentInputMethod().id();
  // Save the new input method id depending on the current browser state.
  switch (state_) {
    case InputMethodManager::STATE_LOGIN_SCREEN:
      if (!InputMethodUtil::IsKeyboardLayout(current_input_method)) {
        DVLOG(1) << "Only keyboard layouts are supported: "
                 << current_input_method;
        return;
      }
      UpdateLocalState(current_input_method);
      return;
    case InputMethodManager::STATE_BROWSER_SCREEN:
      UpdateUserPreferences(current_input_method);
      return;
    case InputMethodManager::STATE_LOCK_SCREEN:
      // We use a special set of input methods on the screen. Do not update.
      return;
    case InputMethodManager::STATE_TERMINATING:
      return;
  }
  NOTREACHED();
}

void BrowserStateMonitor::InputMethodPropertyChanged(
    InputMethodManager* manager) {}

void BrowserStateMonitor::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case chrome::NOTIFICATION_APP_TERMINATING: {
      SetState(InputMethodManager::STATE_TERMINATING);
      break;
    }
    case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
      // The user logged in, but the browser window for user session is not yet
      // ready. An initial input method hasn't been set to the manager.
      // Note that the notification is also sent when Chrome crashes/restarts
      // as of writing, but it might be changed in the future (therefore we need
      // to listen to NOTIFICATION_SESSION_STARTED as well.)
      DVLOG(1) << "Received chrome::NOTIFICATION_LOGIN_USER_CHANGED";
      SetState(InputMethodManager::STATE_BROWSER_SCREEN);
      break;
    }
    case chrome::NOTIFICATION_SESSION_STARTED: {
      // The user logged in, and the browser window for user session is ready.
      // An initial input method has already been set.
      // We should NOT call InitializePrefMembers() here since the notification
      // is sent in the PreProfileInit phase in case when Chrome crashes and
      // restarts.
      DVLOG(1) << "Received chrome::NOTIFICATION_SESSION_STARTED";
      SetState(InputMethodManager::STATE_BROWSER_SCREEN);
      break;
    }
    case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
      const bool is_screen_locked = *content::Details<bool>(details).ptr();
      SetState(is_screen_locked ? InputMethodManager::STATE_LOCK_SCREEN :
               InputMethodManager::STATE_BROWSER_SCREEN);
      break;
    }
    default: {
      NOTREACHED();
      break;
    }
  }
  // Note: browser notifications are sent in the following order.
  //
  // Normal login:
  // 1. chrome::NOTIFICATION_LOGIN_USER_CHANGED is sent.
  // 2. Preferences::NotifyPrefChanged() is called. preload_engines (which
  //    might change the current input method) and current/previous input method
  //    are sent to the manager.
  // 3. chrome::NOTIFICATION_SESSION_STARTED is sent.
  //
  // Chrome crash/restart (after logging in):
  // 1. chrome::NOTIFICATION_LOGIN_USER_CHANGED might be sent.
  // 2. chrome::NOTIFICATION_SESSION_STARTED is sent.
  // 3. Preferences::NotifyPrefChanged() is called. The same things as above
  //    happen.
  //
  // We have to be careful not to overwrite both local and user prefs when
  // preloaded engine is set. Note that it does not work to do nothing in
  // InputMethodChanged() between chrome::NOTIFICATION_LOGIN_USER_CHANGED and
  // chrome::NOTIFICATION_SESSION_STARTED because SESSION_STARTED is sent very
  // early on Chrome crash/restart.
}

void BrowserStateMonitor::SetState(InputMethodManager::State new_state) {
  const InputMethodManager::State old_state = state_;
  state_ = new_state;
  if (old_state != state_)
    manager_->SetState(state_);
}

}  // namespace input_method
}  // namespace chromeos