Skip to content

Commit

Permalink
Update IME preferences outside the IME status button when Uber Tray i…
Browse files Browse the repository at this point in the history
…s in use.

* browser_state_monitor.cc/h:

Added. The class which monitors a notification from the browser to keep track of the browser state (not logged in, logged in, etc.) and notify the current state to the input method manager. The class also updates the appropriate Chrome prefs (~/Local\ State or ~/Preferences) depending on the current browser state.

When Uber Tray is disabled via chrome://flags, the class does nothing. In the next CL, I'll remove all preference code from InputMethodButton and make browser_state_monitor.cc work regardless of whether Uber Tray is disabled or not.

* preferences.cc:

When Uber Tray is enabled, handle prefs::kLanguageCurrentInputMethod and prefs::kLanguagePreviousInputMethod prefs in the class.  preferences.cc is the standard place for checking the initial values of Chrome OS specific user prefs.

BUG=chromiun-os:28297
TEST=manual (not ready for writing unit tests. It will be done as part of 19655 very soon for R20). Checked test cases in http://code.google.com/p/chromium-os/issues/detail?id=19655#c11.

Review URL: https://chromiumcodereview.appspot.com/9852008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128889 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
yusukes@google.com committed Mar 26, 2012
1 parent 913b821 commit 146180a
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 1 deletion.
203 changes: 203 additions & 0 deletions chrome/browser/chromeos/input_method/browser_state_monitor.cc
@@ -0,0 +1,203 @@
// 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 "ash/ash_switches.h"
#include "base/command_line.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),
initialized_(false) {
// On R19, when Uber Tray is disabled, the IME status button will update the
// Preferences.
// TODO(yusukes): Remove all Preferences code from the button on R20.
if (CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kDisableAshUberTray))
return;

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 APP_EXITING here since logout might be canceled by
// JavaScript after APP_EXITING is sent (crosbug.com/11055).
notification_registrar_.Add(this,
content::NOTIFICATION_APP_TERMINATING,
content::NotificationService::AllSources());

// TODO(yusukes): Tell the initial state to the manager.
manager_->AddPreLoginPreferenceObserver(this);
manager_->AddPostLoginPreferenceObserver(this);
}

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

void BrowserStateMonitor::UpdateLocalState(
const std::string& current_input_method) {
if (!InputMethodUtil::IsKeyboardLayout(current_input_method)) {
LOG(ERROR) << "Only keyboard layouts are supported: "
<< current_input_method;
return;
}

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) {
InitializePrefMembers();
const std::string current_input_method_on_pref =
current_input_method_pref_.GetValue();
if (current_input_method_on_pref == current_input_method)
return;
previous_input_method_pref_.SetValue(current_input_method_on_pref);
current_input_method_pref_.SetValue(current_input_method);
}

void BrowserStateMonitor::PreferenceUpdateNeeded(
input_method::InputMethodManager* manager,
const input_method::InputMethodDescriptor& previous_input_method,
const input_method::InputMethodDescriptor& current_input_method) {
DCHECK_EQ(manager_, manager);
// Save the new input method id depending on the current browser state.
switch (state_) {
case InputMethodManager::STATE_LOGIN_SCREEN:
UpdateLocalState(current_input_method.id());
return;
case InputMethodManager::STATE_BROWSER_SCREEN:
UpdateUserPreferences(current_input_method.id());
return;
case InputMethodManager::STATE_LOGGING_IN:
// Do not update the prefs since Preferences::NotifyPrefChanged() will
// notify the current/previous input method IDs to the manager.
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::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::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.
SetState(InputMethodManager::STATE_LOGGING_IN);
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.
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;
}
}

// TODO(yusukes): On R20, we should handle Chrome crash/restart correctly.
// Currently, a wrong input method could be selected when Chrome crashes and
// restarts.
//
// Normal login sequence:
// 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) sequence:
// 1. chrome::NOTIFICATION_LOGIN_USER_CHANGED is sent.
// 2. chrome::NOTIFICATION_SESSION_STARTED is sent.
// 3. Preferences::NotifyPrefChanged() is called. preload_engines (which
// might change the current input method) and current/previous input method
// are sent to the manager. When preload_engines are sent to the manager,
// UpdateUserPreferences() might be accidentally called.
}

void BrowserStateMonitor::SetState(InputMethodManager::State new_state) {
const InputMethodManager::State old_state = state_;
state_ = new_state;
if (old_state != state_) {
// TODO(yusukes): Tell the new state to the manager.
}
}

void BrowserStateMonitor::InitializePrefMembers() {
if (initialized_)
return;

initialized_ = true;
PrefService* pref_service = GetPrefService();
DCHECK(pref_service);
DCHECK_EQ(InputMethodManager::STATE_BROWSER_SCREEN, state_);
previous_input_method_pref_.Init(
prefs::kLanguagePreviousInputMethod, pref_service, this);
current_input_method_pref_.Init(
prefs::kLanguageCurrentInputMethod, pref_service, this);
}

} // namespace input_method
} // namespace chromeos
74 changes: 74 additions & 0 deletions chrome/browser/chromeos/input_method/browser_state_monitor.h
@@ -0,0 +1,74 @@
// 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.

#ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_BROWSER_STATE_MONITOR_H_
#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_BROWSER_STATE_MONITOR_H_
#pragma once

#include <string>

#include "chrome/browser/chromeos/input_method/input_method_manager.h"
#include "chrome/browser/prefs/pref_member.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_types.h"

namespace chromeos {
namespace input_method {

// A class which monitors a notification from the browser to keep track of the
// browser state (not logged in, logged in, etc.) and notify the current state
// to the input method manager. The class also updates the appropriate Chrome
// prefs (~/Local\ State or ~/Preferences) depending on the current browser
// state.
class BrowserStateMonitor
: public content::NotificationObserver,
public input_method::InputMethodManager::PreferenceObserver {
public:
explicit BrowserStateMonitor(InputMethodManager* manager);
virtual ~BrowserStateMonitor();

protected:
// Updates ~/Local\ State file. protected: for testing.
virtual void UpdateLocalState(const std::string& current_input_method);
// Updates ~/Preferences file. protected: for testing.
virtual void UpdateUserPreferences(const std::string& current_input_method);

private:
// InputMethodManager::PreferenceObserver implementation.
// TODO(yusukes): On R20, use input_method::InputMethodManager::Observer and
// remove the PreferenceObserver interface from InputMethodManager.
virtual void PreferenceUpdateNeeded(
input_method::InputMethodManager* manager,
const input_method::InputMethodDescriptor& previous_input_method,
const input_method::InputMethodDescriptor& current_input_method) OVERRIDE;
virtual void FirstObserverIsAdded(
input_method::InputMethodManager* manager) OVERRIDE {}

// content::NotificationObserver overrides:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;

void SetState(InputMethodManager::State new_state);
void InitializePrefMembers();

InputMethodManager* manager_;
InputMethodManager::State state_;

// Objects for updating the Chrome prefs.
StringPrefMember previous_input_method_pref_;
StringPrefMember current_input_method_pref_;
bool initialized_;

// This is used to register this object to some browser notifications.
content::NotificationRegistrar notification_registrar_;

DISALLOW_COPY_AND_ASSIGN(BrowserStateMonitor);
};

} // namespace input_method
} // namespace chromeos

#endif // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_BROWSER_STATE_MONITOR_H_
6 changes: 6 additions & 0 deletions chrome/browser/chromeos/input_method/input_method_manager.cc
Expand Up @@ -17,6 +17,7 @@
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/input_method/browser_state_monitor.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/input_method/input_method_whitelist.h"
#include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
Expand Down Expand Up @@ -83,6 +84,7 @@ class InputMethodManagerImpl : public InputMethodManager,
ibus_daemon_process_handle_(base::kNullProcessHandle),
util_(whitelist_.GetSupportedInputMethods()),
xkeyboard_(XKeyboard::Create(util_)),
ALLOW_THIS_IN_INITIALIZER_LIST(browser_state_monitor_(this)),
ignore_hotkeys_(false) {
// Observe APP_TERMINATING to stop input method daemon gracefully.
// We should not use APP_EXITING here since logout might be canceled by
Expand Down Expand Up @@ -1289,6 +1291,10 @@ class InputMethodManagerImpl : public InputMethodManager,
// auto-repeat interval.
scoped_ptr<XKeyboard> xkeyboard_;

// An object which monitors a notification from the browser to keep track of
// the browser state (not logged in, logged in, etc.).
BrowserStateMonitor browser_state_monitor_;

// true when DisableHotkeys() is called to temporarily disable IME hotkeys.
// EnableHotkeys() resets the flag to the default value, false.
bool ignore_hotkeys_;
Expand Down
11 changes: 11 additions & 0 deletions chrome/browser/chromeos/input_method/input_method_manager.h
Expand Up @@ -38,6 +38,17 @@ class XKeyboard;
// InputMethodManager::GetInstance().
class InputMethodManager {
public:
enum State {
STATE_LOGIN_SCREEN = 0,
// The user entered the correct password (= NOTIFICATION_LOGIN_USER_CHANGED
// has been sent), but NOTIFICATION_SESSION_STARTED has not.
STATE_LOGGING_IN,
// The browser window for user session is ready.
STATE_BROWSER_SCREEN,
STATE_LOCK_SCREEN,
STATE_TERMINATING,
};

class Observer {
public:
virtual ~Observer() {}
Expand Down
27 changes: 26 additions & 1 deletion chrome/browser/chromeos/preferences.cc
Expand Up @@ -4,6 +4,7 @@

#include "chrome/browser/chromeos/preferences.h"

#include "ash/ash_switches.h"
#include "base/chromeos/chromeos_version.h"
#include "base/command_line.h"
#include "base/i18n/time_formatting.h"
Expand Down Expand Up @@ -266,8 +267,11 @@ void Preferences::Init(PrefService* prefs) {
hotkey_previous_engine_.Init(
prefs::kLanguageHotkeyPreviousEngine, prefs, this);
preferred_languages_.Init(prefs::kLanguagePreferredLanguages,
prefs, this);
prefs, this);
preload_engines_.Init(prefs::kLanguagePreloadEngines, prefs, this);
current_input_method_.Init(prefs::kLanguageCurrentInputMethod, prefs, this);
previous_input_method_.Init(prefs::kLanguagePreviousInputMethod, prefs, this);

for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) {
chewing_boolean_prefs_[i].Init(
language_prefs::kChewingBooleanPrefs[i].pref_name, prefs, this);
Expand Down Expand Up @@ -423,6 +427,27 @@ void Preferences::NotifyPrefChanged(const std::string* pref_name) {
language_prefs::kPreloadEnginesConfigName,
preload_engines_.GetValue());
}

// Do not check |*pref_name| for the two prefs. We're only interested in
// initial values of the prefs.
// TODO(yusukes): Remove the second condition on R20.
if (!pref_name && !CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kDisableAshUberTray)) {
const std::string previous_input_method_id =
previous_input_method_.GetValue();
const std::string current_input_method_id =
current_input_method_.GetValue();
// NOTICE: ChangeInputMethod() has to be called AFTER the value of
// |preload_engines_| is sent to the InputMethodManager. Otherwise, the
// ChangeInputMethod request might be ignored as an invalid input method ID.
input_method::InputMethodManager* manager =
input_method::InputMethodManager::GetInstance();
if (!previous_input_method_id.empty())
manager->ChangeInputMethod(previous_input_method_id);
if (!current_input_method_id.empty())
manager->ChangeInputMethod(current_input_method_id);
}

for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) {
if (!pref_name ||
*pref_name == language_prefs::kChewingBooleanPrefs[i].pref_name) {
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/chromeos/preferences.h
Expand Up @@ -104,6 +104,8 @@ class Preferences : public content::NotificationObserver {
StringPrefMember hotkey_previous_engine_;
StringPrefMember preferred_languages_;
StringPrefMember preload_engines_;
StringPrefMember current_input_method_;
StringPrefMember previous_input_method_;

BooleanPrefMember chewing_boolean_prefs_[
language_prefs::kNumChewingBooleanPrefs];
Expand Down
2 changes: 2 additions & 0 deletions chrome/chrome_browser.gypi
Expand Up @@ -566,6 +566,8 @@
'browser/chromeos/imageburner/burn_controller.h',
'browser/chromeos/imageburner/burn_manager.cc',
'browser/chromeos/imageburner/burn_manager.h',
'browser/chromeos/input_method/browser_state_monitor.cc',
'browser/chromeos/input_method/browser_state_monitor.h',
'browser/chromeos/input_method/candidate_window.cc',
'browser/chromeos/input_method/candidate_window.h',
'browser/chromeos/input_method/ibus_controller.cc',
Expand Down

0 comments on commit 146180a

Please sign in to comment.