blob: 3de50d30b3ae84e50385a616d97fb9fd6421a901 [file] [log] [blame]
// 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;
}
case chrome::NOTIFICATION_PREF_CHANGED: {
break; // just ignore the notification.
}
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