browser_state_monitor.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/browser_state_monitor.h" 6 7#include "chrome/browser/browser_process.h" 8#include "chrome/browser/chromeos/input_method/input_method_util.h" 9#include "chrome/browser/chromeos/language_preferences.h" 10#include "chrome/browser/prefs/pref_service.h" 11#include "chrome/browser/profiles/profile_manager.h" 12#include "chrome/common/chrome_notification_types.h" 13#include "chrome/common/pref_names.h" 14#include "content/public/browser/notification_service.h" 15 16namespace chromeos { 17namespace input_method { 18namespace { 19 20PrefService* GetPrefService() { 21 Profile* profile = ProfileManager::GetDefaultProfile(); 22 if (profile) 23 return profile->GetPrefs(); 24 return NULL; 25} 26 27} // namespace 28 29BrowserStateMonitor::BrowserStateMonitor(InputMethodManager* manager) 30 : manager_(manager), 31 state_(InputMethodManager::STATE_LOGIN_SCREEN), 32 pref_service_(NULL) { 33 notification_registrar_.Add(this, 34 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 35 content::NotificationService::AllSources()); 36 notification_registrar_.Add(this, 37 chrome::NOTIFICATION_SESSION_STARTED, 38 content::NotificationService::AllSources()); 39 notification_registrar_.Add(this, 40 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 41 content::NotificationService::AllSources()); 42 // We should not use ALL_BROWSERS_CLOSING here since logout might be cancelled 43 // by JavaScript after ALL_BROWSERS_CLOSING is sent (crosbug.com/11055). 44 notification_registrar_.Add(this, 45 chrome::NOTIFICATION_APP_TERMINATING, 46 content::NotificationService::AllSources()); 47 48 manager_->SetState(state_); 49 manager_->AddObserver(this); 50} 51 52BrowserStateMonitor::~BrowserStateMonitor() { 53 manager_->RemoveObserver(this); 54} 55 56void BrowserStateMonitor::SetPrefServiceForTesting(PrefService* pref_service) { 57 pref_service_ = pref_service; 58} 59 60void BrowserStateMonitor::UpdateLocalState( 61 const std::string& current_input_method) { 62 if (!g_browser_process || !g_browser_process->local_state()) 63 return; 64 g_browser_process->local_state()->SetString( 65 language_prefs::kPreferredKeyboardLayout, 66 current_input_method); 67} 68 69void BrowserStateMonitor::UpdateUserPreferences( 70 const std::string& current_input_method) { 71 PrefService* pref_service = pref_service_ ? pref_service_ : GetPrefService(); 72 DCHECK(pref_service); 73 74 // Even though we're DCHECK'ing to catch this on debug builds, we don't 75 // want to crash a release build in case the pref service is no longer 76 // available. 77 if (!pref_service) 78 return; 79 80 const std::string current_input_method_on_pref = 81 pref_service->GetString(prefs::kLanguageCurrentInputMethod); 82 if (current_input_method_on_pref == current_input_method) 83 return; 84 85 pref_service->SetString(prefs::kLanguagePreviousInputMethod, 86 current_input_method_on_pref); 87 pref_service->SetString(prefs::kLanguageCurrentInputMethod, 88 current_input_method); 89} 90 91void BrowserStateMonitor::InputMethodChanged(InputMethodManager* manager, 92 bool show_message) { 93 DCHECK_EQ(manager_, manager); 94 const std::string current_input_method = 95 manager->GetCurrentInputMethod().id(); 96 // Save the new input method id depending on the current browser state. 97 switch (state_) { 98 case InputMethodManager::STATE_LOGIN_SCREEN: 99 if (!InputMethodUtil::IsKeyboardLayout(current_input_method)) { 100 DVLOG(1) << "Only keyboard layouts are supported: " 101 << current_input_method; 102 return; 103 } 104 UpdateLocalState(current_input_method); 105 return; 106 case InputMethodManager::STATE_BROWSER_SCREEN: 107 UpdateUserPreferences(current_input_method); 108 return; 109 case InputMethodManager::STATE_LOCK_SCREEN: 110 // We use a special set of input methods on the screen. Do not update. 111 return; 112 case InputMethodManager::STATE_TERMINATING: 113 return; 114 } 115 NOTREACHED(); 116} 117 118void BrowserStateMonitor::InputMethodPropertyChanged( 119 InputMethodManager* manager) {} 120 121void BrowserStateMonitor::Observe( 122 int type, 123 const content::NotificationSource& source, 124 const content::NotificationDetails& details) { 125 switch (type) { 126 case chrome::NOTIFICATION_APP_TERMINATING: { 127 SetState(InputMethodManager::STATE_TERMINATING); 128 break; 129 } 130 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: { 131 // The user logged in, but the browser window for user session is not yet 132 // ready. An initial input method hasn't been set to the manager. 133 // Note that the notification is also sent when Chrome crashes/restarts 134 // as of writing, but it might be changed in the future (therefore we need 135 // to listen to NOTIFICATION_SESSION_STARTED as well.) 136 DVLOG(1) << "Received chrome::NOTIFICATION_LOGIN_USER_CHANGED"; 137 SetState(InputMethodManager::STATE_BROWSER_SCREEN); 138 break; 139 } 140 case chrome::NOTIFICATION_SESSION_STARTED: { 141 // The user logged in, and the browser window for user session is ready. 142 // An initial input method has already been set. 143 // We should NOT call InitializePrefMembers() here since the notification 144 // is sent in the PreProfileInit phase in case when Chrome crashes and 145 // restarts. 146 DVLOG(1) << "Received chrome::NOTIFICATION_SESSION_STARTED"; 147 SetState(InputMethodManager::STATE_BROWSER_SCREEN); 148 break; 149 } 150 case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: { 151 const bool is_screen_locked = *content::Details<bool>(details).ptr(); 152 SetState(is_screen_locked ? InputMethodManager::STATE_LOCK_SCREEN : 153 InputMethodManager::STATE_BROWSER_SCREEN); 154 break; 155 } 156 default: { 157 NOTREACHED(); 158 break; 159 } 160 } 161 // Note: browser notifications are sent in the following order. 162 // 163 // Normal login: 164 // 1. chrome::NOTIFICATION_LOGIN_USER_CHANGED is sent. 165 // 2. Preferences::NotifyPrefChanged() is called. preload_engines (which 166 // might change the current input method) and current/previous input method 167 // are sent to the manager. 168 // 3. chrome::NOTIFICATION_SESSION_STARTED is sent. 169 // 170 // Chrome crash/restart (after logging in): 171 // 1. chrome::NOTIFICATION_LOGIN_USER_CHANGED might be sent. 172 // 2. chrome::NOTIFICATION_SESSION_STARTED is sent. 173 // 3. Preferences::NotifyPrefChanged() is called. The same things as above 174 // happen. 175 // 176 // We have to be careful not to overwrite both local and user prefs when 177 // preloaded engine is set. Note that it does not work to do nothing in 178 // InputMethodChanged() between chrome::NOTIFICATION_LOGIN_USER_CHANGED and 179 // chrome::NOTIFICATION_SESSION_STARTED because SESSION_STARTED is sent very 180 // early on Chrome crash/restart. 181} 182 183void BrowserStateMonitor::SetState(InputMethodManager::State new_state) { 184 const InputMethodManager::State old_state = state_; 185 state_ = new_state; 186 if (old_state != state_) 187 manager_->SetState(state_); 188} 189 190} // namespace input_method 191} // namespace chromeos 192