diff options
Diffstat (limited to 'chrome/browser/chromeos')
318 files changed, 11515 insertions, 8532 deletions
diff --git a/chrome/browser/chromeos/DEPS b/chrome/browser/chromeos/DEPS index 29b7aa6..b12714a 100644 --- a/chrome/browser/chromeos/DEPS +++ b/chrome/browser/chromeos/DEPS @@ -1,4 +1,3 @@ include_rules = [
- "+third_party/cros",
"+cros",
]
diff --git a/chrome/browser/chromeos/PRESUBMIT.py b/chrome/browser/chromeos/PRESUBMIT.py index 5762625..430c0f6 100755 --- a/chrome/browser/chromeos/PRESUBMIT.py +++ b/chrome/browser/chromeos/PRESUBMIT.py @@ -1,4 +1,4 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Copyright (c) 2010 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. @@ -9,4 +9,4 @@ for more details on the presubmit API built into gcl. """ def GetPreferredTrySlaves(): - return ['linux_chromeos'] + return ['linux_view', 'linux_chromeos'] diff --git a/chrome/browser/chromeos/audio_handler.cc b/chrome/browser/chromeos/audio_handler.cc index f3157ee..f069b18 100644 --- a/chrome/browser/chromeos/audio_handler.cc +++ b/chrome/browser/chromeos/audio_handler.cc @@ -21,6 +21,8 @@ const double kMaxVolumeDb = 6.0; // A value of less than one adjusts quieter volumes in larger steps (giving // finer resolution in the higher volumes). const double kVolumeBias = 0.5; +// If a connection is lost, we try again this many times +const int kMaxReconnectTries = 4; } // namespace @@ -28,11 +30,9 @@ const double kVolumeBias = 0.5; // handles the volume level logic. // TODO(davej): Serialize volume/mute for next startup? -// TODO(davej): Should we try to regain a connection if for some reason all was -// initialized fine, but later IsValid() returned false? Maybe N retries? -double AudioHandler::GetVolumePercent() const { - if (!SanityCheck()) +double AudioHandler::GetVolumePercent() { + if (!VerifyMixerConnection()) return 0; return VolumeDbToPercent(mixer_->GetVolumeDb()); @@ -41,7 +41,7 @@ double AudioHandler::GetVolumePercent() const { // Set volume using our internal 0-100% range. Notice 0% is a special case of // silence, so we set the mixer volume to kSilenceDb instead of kMinVolumeDb. void AudioHandler::SetVolumePercent(double volume_percent) { - if (!SanityCheck()) + if (!VerifyMixerConnection()) return; DCHECK(volume_percent >= 0.0); @@ -55,7 +55,7 @@ void AudioHandler::SetVolumePercent(double volume_percent) { } void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { - if (!SanityCheck()) + if (!VerifyMixerConnection()) return; DLOG(INFO) << "Adjusting Volume by " << adjust_by_percent << " percent"; @@ -79,15 +79,15 @@ void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { mixer_->SetVolumeDb(new_volume); } -bool AudioHandler::IsMute() const { - if (!SanityCheck()) +bool AudioHandler::IsMute() { + if (!VerifyMixerConnection()) return false; return mixer_->IsMute(); } void AudioHandler::SetMute(bool do_mute) { - if (!SanityCheck()) + if (!VerifyMixerConnection()) return; DLOG(INFO) << "Setting Mute to " << do_mute; @@ -101,7 +101,8 @@ void AudioHandler::OnMixerInitialized(bool success) { } AudioHandler::AudioHandler() - : connected_(false) { + : connected_(false), + reconnect_tries_(0) { mixer_.reset(new PulseAudioMixer()); if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) { LOG(ERROR) << "Unable to connect to PulseAudio"; @@ -109,22 +110,39 @@ AudioHandler::AudioHandler() } AudioHandler::~AudioHandler() { + mixer_.reset(); }; -inline bool AudioHandler::SanityCheck() const { - if (!mixer_->IsValid()) { +bool AudioHandler::VerifyMixerConnection() { + PulseAudioMixer::State mixer_state = mixer_->CheckState(); + if (mixer_state == PulseAudioMixer::READY) + return true; + if (connected_) { + // Something happened and the mixer is no longer valid after having been + // initialized earlier. + connected_ = false; + LOG(ERROR) << "Lost connection to PulseAudio"; + } else { + LOG(ERROR) << "Mixer not valid"; + } + + if ((mixer_state == PulseAudioMixer::INITIALIZING) || + (mixer_state == PulseAudioMixer::SHUTTING_DOWN)) + return false; + + if (reconnect_tries_ < kMaxReconnectTries) { + reconnect_tries_++; + LOG(INFO) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ + << "/" << kMaxReconnectTries; + mixer_.reset(new PulseAudioMixer()); + connected_ = mixer_->InitSync(); if (connected_) { - // Something happened and the mixer is no longer valid after having been - // initialized earlier. - // TODO(davej): Try to reconnect now? - connected_ = false; - LOG(ERROR) << "Lost connection to PulseAudio"; - } else { - LOG(ERROR) << "Mixer not valid"; + reconnect_tries_ = 0; + return true; } - return false; + LOG(ERROR) << "Unable to re-connect to PulseAudio"; } - return true; + return false; } // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us diff --git a/chrome/browser/chromeos/audio_handler.h b/chrome/browser/chromeos/audio_handler.h index 7e443a6..67addcb 100644 --- a/chrome/browser/chromeos/audio_handler.h +++ b/chrome/browser/chromeos/audio_handler.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_AUDIO_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_AUDIO_HANDLER_H_ +#pragma once #include "base/scoped_ptr.h" #include "base/singleton.h" @@ -22,7 +23,7 @@ class AudioHandler { // Volume may go above 100% if another process changes PulseAudio's volume. // Returns default of 0 on error. This function will block until the volume // is retrieved or fails. Blocking call. - double GetVolumePercent() const; + double GetVolumePercent(); // Set volume level from 0-100%. Volumes above 100% are OK and boost volume, // although clipping will occur more at higher volumes. Volume gets quieter @@ -36,7 +37,7 @@ class AudioHandler { // Just returns true if mute, false if not or an error occurred. // Blocking call. - bool IsMute() const; + bool IsMute(); // Mutes all audio. Non-blocking call. void SetMute(bool do_mute); @@ -50,14 +51,15 @@ class AudioHandler { AudioHandler(); virtual ~AudioHandler(); - inline bool SanityCheck() const; + bool VerifyMixerConnection(); // Conversion between our internal scaling (0-100%) and decibels. static double VolumeDbToPercent(double volume_db); static double PercentToVolumeDb(double volume_percent); scoped_ptr<PulseAudioMixer> mixer_; - mutable bool connected_; // Mutable for sanity checking only + bool connected_; + int reconnect_tries_; DISALLOW_COPY_AND_ASSIGN(AudioHandler); }; diff --git a/chrome/browser/chromeos/boot_times_loader.cc b/chrome/browser/chromeos/boot_times_loader.cc index acdadce..c6b2628 100644 --- a/chrome/browser/chromeos/boot_times_loader.cc +++ b/chrome/browser/chromeos/boot_times_loader.cc @@ -13,7 +13,9 @@ #include "base/message_loop.h" #include "base/process_util.h" #include "base/singleton.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "base/thread.h" #include "base/time.h" #include "chrome/browser/browser_process.h" @@ -96,7 +98,7 @@ static bool GetTime(const std::string& log, double* value) { size_t chars_left = space_index != std::string::npos ? space_index : std::string::npos; std::string value_string = contents.substr(0, chars_left); - return StringToDouble(value_string, value); + return base::StringToDouble(value_string, value); } return false; } @@ -134,16 +136,16 @@ static void SendBootTimesToUMA(const BootTimesLoader::BootTimes& boot_times) { // Stores the boot times to a file in /tmp to indicate that the // times for the most recent boot event have been reported // already. The file will be deleted at system shutdown/reboot. - std::string boot_times_text = StringPrintf("total: %.2f\n" - "firmware: %.2f\n" - "kernel: %.2f\n" - "system: %.2f\n" - "chrome: %.2f\n", - boot_times.total, - boot_times.firmware, - boot_times.pre_startup, - boot_times.system, - boot_times.chrome); + std::string boot_times_text = base::StringPrintf("total: %.2f\n" + "firmware: %.2f\n" + "kernel: %.2f\n" + "system: %.2f\n" + "chrome: %.2f\n", + boot_times.total, + boot_times.firmware, + boot_times.pre_startup, + boot_times.system, + boot_times.chrome); file_util::WriteFile(sent, boot_times_text.data(), boot_times_text.size()); DCHECK(file_util::PathExists(sent)); } @@ -219,7 +221,7 @@ static void WriteLoginTimes( base::Time chrome_first_render) { const FilePath log_path(kLogPath); std::string output = - StringPrintf("total: %.2f\nauth: %.2f\nlogin: %.2f\n", + base::StringPrintf("total: %.2f\nauth: %.2f\nlogin: %.2f\n", (chrome_first_render - login_attempt).InSecondsF(), (login_success - login_attempt).InSecondsF(), (chrome_first_render - login_success).InSecondsF()); diff --git a/chrome/browser/chromeos/boot_times_loader.h b/chrome/browser/chromeos/boot_times_loader.h index 143e425..95dd394 100644 --- a/chrome/browser/chromeos/boot_times_loader.h +++ b/chrome/browser/chromeos/boot_times_loader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_BOOT_TIMES_LOADER_H_ #define CHROME_BROWSER_CHROMEOS_BOOT_TIMES_LOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/cros/cros_api.gyp b/chrome/browser/chromeos/cros/cros_api.gyp deleted file mode 100644 index e69de29..0000000 --- a/chrome/browser/chromeos/cros/cros_api.gyp +++ /dev/null diff --git a/chrome/browser/chromeos/cros/cros_in_process_browser_test.cc b/chrome/browser/chromeos/cros/cros_in_process_browser_test.cc index 58055b5..ad88ae9 100644 --- a/chrome/browser/chromeos/cros/cros_in_process_browser_test.cc +++ b/chrome/browser/chromeos/cros/cros_in_process_browser_test.cc @@ -3,299 +3,19 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" - -#include "base/message_loop.h" -#include "base/ref_counted.h" -#include "base/time.h" -#include "chrome/browser/browser.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" -#include "chrome/browser/chromeos/cros/mock_keyboard_library.h" -#include "chrome/browser/chromeos/cros/mock_input_method_library.h" -#include "chrome/browser/chromeos/cros/mock_library_loader.h" -#include "chrome/browser/chromeos/cros/mock_network_library.h" -#include "chrome/browser/chromeos/cros/mock_power_library.h" -#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h" -#include "chrome/browser/chromeos/cros/mock_synaptics_library.h" -#include "chrome/browser/chromeos/cros/mock_system_library.h" -#include "chrome/browser/chromeos/login/wizard_controller.h" -#include "chrome/browser/chromeos/login/wizard_screen.h" #include "chrome/test/in_process_browser_test.h" -#include "chrome/test/ui_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gmock/include/gmock/gmock.h" namespace chromeos { -using ::testing::AnyNumber; -using ::testing::InvokeWithoutArgs; -using ::testing::Return; -using ::testing::ReturnRef; -using ::testing::StrictMock; -using ::testing::_; - -CrosInProcessBrowserTest::CrosInProcessBrowserTest() - : loader_(NULL), - mock_cryptohome_library_(NULL), - mock_keyboard_library_(NULL), - mock_input_method_library_(NULL), - mock_network_library_(NULL), - mock_power_library_(NULL), - mock_screen_lock_library_(NULL), - mock_synaptics_library_(NULL), - mock_system_library_(NULL) {} - -CrosInProcessBrowserTest::~CrosInProcessBrowserTest() { -} - -chromeos::CrosLibrary::TestApi* CrosInProcessBrowserTest::test_api() { - return chromeos::CrosLibrary::Get()->GetTestApi(); -} - -void CrosInProcessBrowserTest::InitStatusAreaMocks() { - InitMockKeyboardLibrary(); - InitMockInputMethodLibrary(); - InitMockNetworkLibrary(); - InitMockPowerLibrary(); - InitMockSynapticsLibrary(); - InitMockSystemLibrary(); -} - -void CrosInProcessBrowserTest::InitMockLibraryLoader() { - if (loader_) - return; - loader_ = new StrictMock<MockLibraryLoader>(); - EXPECT_CALL(*loader_, Load(_)) - .Times(AnyNumber()) - .WillRepeatedly(Return(true)); - test_api()->SetLibraryLoader(loader_, true); -} - -void CrosInProcessBrowserTest::InitMockCryptohomeLibrary() { - InitMockLibraryLoader(); - if (mock_cryptohome_library_) - return; - mock_cryptohome_library_ = new StrictMock<MockCryptohomeLibrary>(); - test_api()->SetCryptohomeLibrary(mock_cryptohome_library_, true); -} - -void CrosInProcessBrowserTest::InitMockKeyboardLibrary() { - InitMockLibraryLoader(); - if (mock_keyboard_library_) - return; - mock_keyboard_library_ = new StrictMock<MockKeyboardLibrary>(); - test_api()->SetKeyboardLibrary(mock_keyboard_library_, true); -} - -void CrosInProcessBrowserTest::InitMockInputMethodLibrary() { - InitMockLibraryLoader(); - if (mock_input_method_library_) - return; - mock_input_method_library_ = new StrictMock<MockInputMethodLibrary>(); - test_api()->SetInputMethodLibrary(mock_input_method_library_, true); -} - -void CrosInProcessBrowserTest::InitMockNetworkLibrary() { - InitMockLibraryLoader(); - if (mock_network_library_) - return; - mock_network_library_ = new StrictMock<MockNetworkLibrary>(); - test_api()->SetNetworkLibrary(mock_network_library_, true); -} - -void CrosInProcessBrowserTest::InitMockPowerLibrary() { - InitMockLibraryLoader(); - if (mock_power_library_) - return; - mock_power_library_ = new StrictMock<MockPowerLibrary>(); - test_api()->SetPowerLibrary(mock_power_library_, true); -} - -void CrosInProcessBrowserTest::InitMockScreenLockLibrary() { - InitMockLibraryLoader(); - if (mock_screen_lock_library_) - return; - mock_screen_lock_library_ = new StrictMock<MockScreenLockLibrary>(); - test_api()->SetScreenLockLibrary(mock_screen_lock_library_, true); -} - -void CrosInProcessBrowserTest::InitMockSynapticsLibrary() { - InitMockLibraryLoader(); - if (mock_synaptics_library_) - return; - mock_synaptics_library_ = new StrictMock<MockSynapticsLibrary>(); - test_api()->SetSynapticsLibrary(mock_synaptics_library_, true); -} - -void CrosInProcessBrowserTest::InitMockSystemLibrary() { - InitMockLibraryLoader(); - if (mock_system_library_) - return; - mock_system_library_ = new StrictMock<MockSystemLibrary>(); - test_api()->SetSystemLibrary(mock_system_library_, true); -} - -void CrosInProcessBrowserTest::SetStatusAreaMocksExpectations() { - SetKeyboardLibraryStatusAreaExpectations(); - SetInputMethodLibraryStatusAreaExpectations(); - SetNetworkLibraryStatusAreaExpectations(); - SetPowerLibraryStatusAreaExpectations(); - SetSynapticsLibraryExpectations(); - SetSystemLibraryStatusAreaExpectations(); -} - -void CrosInProcessBrowserTest::SetKeyboardLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_keyboard_library_, GetCurrentKeyboardLayoutName()) - .Times(AnyNumber()) - .WillRepeatedly((Return("us"))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_keyboard_library_, SetCurrentKeyboardLayoutByName(_)) - .Times(AnyNumber()) - .WillRepeatedly((Return(true))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_keyboard_library_, SetKeyboardLayoutPerWindow(_)) - .Times(AnyNumber()) - .WillRepeatedly((Return(true))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_keyboard_library_, GetKeyboardLayoutPerWindow(_)) - .Times(AnyNumber()) - .WillRepeatedly((Return(true))) - .RetiresOnSaturation(); -} - -void CrosInProcessBrowserTest::SetInputMethodLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_input_method_library_, AddObserver(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_input_method_library_, GetActiveInputMethods()) - .Times(AnyNumber()) - .WillRepeatedly(InvokeWithoutArgs(CreateFallbackInputMethodDescriptors)) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_input_method_library_, GetSupportedInputMethods()) - .Times(AnyNumber()) - .WillRepeatedly(InvokeWithoutArgs(CreateFallbackInputMethodDescriptors)) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_input_method_library_, current_ime_properties()) - .Times(1) - .WillOnce((ReturnRef(ime_properties_))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_input_method_library_, SetImeConfig(_, _, _)) - .Times(AnyNumber()) - .WillRepeatedly((Return(true))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_input_method_library_, RemoveObserver(_)) - .Times(1) - .RetiresOnSaturation(); -} - -void CrosInProcessBrowserTest::SetNetworkLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_network_library_, AddObserver(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_connecting()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, wifi_connected()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_connecting()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, cellular_connected()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, ethernet_connected()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, Connected()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, Connecting()) - .Times(1) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_network_library_, RemoveObserver(_)) - .Times(1) - .RetiresOnSaturation(); -} -void CrosInProcessBrowserTest::SetPowerLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_power_library_, AddObserver(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, battery_fully_charged()) - .Times(3) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, battery_is_present()) - .Times(1) - .WillOnce((Return(true))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, battery_percentage()) - .Times(2) - .WillRepeatedly((Return(42.0))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, line_power_on()) - .Times(4) - .WillRepeatedly((Return(false))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, battery_time_to_empty()) - .Times(1) - .WillOnce((Return(base::TimeDelta::FromMinutes(42)))) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_power_library_, RemoveObserver(_)) - .Times(1) - .RetiresOnSaturation(); +CrosInProcessBrowserTest::CrosInProcessBrowserTest() { + cros_mock_.reset(new CrosMock()); } -void CrosInProcessBrowserTest::SetSystemLibraryStatusAreaExpectations() { - EXPECT_CALL(*mock_system_library_, AddObserver(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*mock_system_library_, RemoveObserver(_)) - .Times(1) - .RetiresOnSaturation(); -} - -void CrosInProcessBrowserTest::SetSynapticsLibraryExpectations() { - EXPECT_CALL(*mock_synaptics_library_, SetBoolParameter(_, _)) - .Times(AnyNumber()); - EXPECT_CALL(*mock_synaptics_library_, SetRangeParameter(_, _)) - .Times(AnyNumber()); -} - -void CrosInProcessBrowserTest::SetSystemLibraryExpectations() { - EXPECT_CALL(*mock_system_library_, GetTimezone()) - .Times(AnyNumber()); - EXPECT_CALL(*mock_system_library_, SetTimezone(_)) - .Times(AnyNumber()); +CrosInProcessBrowserTest::~CrosInProcessBrowserTest() { } void CrosInProcessBrowserTest::TearDownInProcessBrowserTestFixture() { - // Prevent bogus gMock leak check from firing. - if (loader_) - test_api()->SetLibraryLoader(NULL, false); - if (mock_cryptohome_library_) - test_api()->SetCryptohomeLibrary(NULL, false); - if (mock_keyboard_library_) - test_api()->SetKeyboardLibrary(NULL, false); - if (mock_input_method_library_) - test_api()->SetInputMethodLibrary(NULL, false); - if (mock_network_library_) - test_api()->SetNetworkLibrary(NULL, false); - if (mock_power_library_) - test_api()->SetPowerLibrary(NULL, false); - if (mock_screen_lock_library_) - test_api()->SetScreenLockLibrary(NULL, false); - if (mock_synaptics_library_) - test_api()->SetSynapticsLibrary(NULL, false); - if (mock_system_library_) - test_api()->SetSystemLibrary(NULL, false); + cros_mock_->TearDownMocks(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/cros_in_process_browser_test.h b/chrome/browser/chromeos/cros/cros_in_process_browser_test.h index 52e11db..b4e9a2e 100644 --- a/chrome/browser/chromeos/cros/cros_in_process_browser_test.h +++ b/chrome/browser/chromeos/cros/cros_in_process_browser_test.h @@ -4,32 +4,19 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_CROS_IN_PROCESS_BROWSER_TEST_H_ #define CHROME_BROWSER_CHROMEOS_CROS_CROS_IN_PROCESS_BROWSER_TEST_H_ +#pragma once #include "base/scoped_ptr.h" -#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/cros_mock.h" #include "chrome/test/in_process_browser_test.h" -#include "cros/chromeos_input_method.h" namespace chromeos { -class MockCryptohomeLibrary; -class MockKeyboardLibrary; -class MockInputMethodLibrary; -class MockLibraryLoader; -class MockNetworkLibrary; -class MockPowerLibrary; -class MockScreenLockLibrary; -class MockScreenLockLibrary; -class MockSynapticsLibrary; -class MockSystemLibrary; - // Base class for Chromium OS tests wanting to bring up a browser in the // unit test process and mock some parts of CrosLibrary. Once you mock part of // CrosLibrary it will be considered as successfully loaded and libraries -// that compose CrosLibrary will be created. The issue here is that you do -// need to specify minimum set of mocks for you test to succeed. -// CrosInProcessBrowserTest defines minimum set of mocks that is used -// by status area elements (network, input language, power). +// that compose CrosLibrary will be created. Use CrosMock to specify minimum +// set of mocks for you test to succeed. // See comments for InProcessBrowserTest base class too. class CrosInProcessBrowserTest : public InProcessBrowserTest { public: @@ -37,62 +24,11 @@ class CrosInProcessBrowserTest : public InProcessBrowserTest { virtual ~CrosInProcessBrowserTest(); protected: - // This method setups basic mocks that are used by status area items: - // LibraryLoader, Language, Network, Power, Synaptics libraries. - // Add call to this method at the beginning of your - // SetUpInProcessBrowserTestFixture. - void InitStatusAreaMocks(); - - // Initialization of CrosLibrary mock loader. If you intend calling - // separate init methods for mocks call this one first. - void InitMockLibraryLoader(); - - // Initialization of mocks. - void InitMockCryptohomeLibrary(); - void InitMockKeyboardLibrary(); - void InitMockInputMethodLibrary(); - void InitMockNetworkLibrary(); - void InitMockPowerLibrary(); - void InitMockScreenLockLibrary(); - void InitMockSynapticsLibrary(); - void InitMockSystemLibrary(); - - // This method setups corresponding expectations for basic mocks that - // are used by status area items. - // Make sure that InitStatusAreaMocks was called before. - // Add call to this method in your SetUpInProcessBrowserTestFixture. - // They are all configured with RetiresOnSaturation(). - // Once such expectation is used it won't block expectations you've defined. - void SetStatusAreaMocksExpectations(); - - // Methods to setup minimal mocks expectations for status area. - void SetKeyboardLibraryStatusAreaExpectations(); - void SetInputMethodLibraryStatusAreaExpectations(); - void SetNetworkLibraryStatusAreaExpectations(); - void SetPowerLibraryStatusAreaExpectations(); - void SetSystemLibraryStatusAreaExpectations(); - void SetSynapticsLibraryExpectations(); - void SetSystemLibraryExpectations(); + scoped_ptr<CrosMock> cros_mock_; // Overriden for things you would normally override TearDown for. virtual void TearDownInProcessBrowserTestFixture(); - // TestApi gives access to CrosLibrary private members. - chromeos::CrosLibrary::TestApi* test_api(); - - // Mocks, destroyed by CrosLibrary class. - MockLibraryLoader* loader_; - MockCryptohomeLibrary* mock_cryptohome_library_; - MockKeyboardLibrary* mock_keyboard_library_; - MockInputMethodLibrary* mock_input_method_library_; - MockNetworkLibrary* mock_network_library_; - MockPowerLibrary* mock_power_library_; - MockScreenLockLibrary* mock_screen_lock_library_; - MockSynapticsLibrary* mock_synaptics_library_; - MockSystemLibrary* mock_system_library_; - - ImePropertyList ime_properties_; - private: DISALLOW_COPY_AND_ASSIGN(CrosInProcessBrowserTest); }; diff --git a/chrome/browser/chromeos/cros/cros_library.cc b/chrome/browser/chromeos/cros/cros_library.cc index a9e469d..9065304 100644 --- a/chrome/browser/chromeos/cros/cros_library.cc +++ b/chrome/browser/chromeos/cros/cros_library.cc @@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/burn_library.h" #include "chrome/browser/chromeos/cros/cros_library_loader.h" #include "chrome/browser/chromeos/cros/cryptohome_library.h" #include "chrome/browser/chromeos/cros/input_method_library.h" @@ -14,77 +15,24 @@ #include "chrome/browser/chromeos/cros/power_library.h" #include "chrome/browser/chromeos/cros/screen_lock_library.h" #include "chrome/browser/chromeos/cros/speech_synthesis_library.h" -#include "chrome/browser/chromeos/cros/synaptics_library.h" #include "chrome/browser/chromeos/cros/syslogs_library.h" #include "chrome/browser/chromeos/cros/system_library.h" +#include "chrome/browser/chromeos/cros/touchpad_library.h" #include "chrome/browser/chromeos/cros/update_library.h" namespace chromeos { CrosLibrary::CrosLibrary() : library_loader_(NULL), - crypto_lib_(NULL), - keyboard_lib_(NULL), - input_method_lib_(NULL), - login_lib_(NULL), - mount_lib_(NULL), - network_lib_(NULL), - power_lib_(NULL), - screen_lock_lib_(NULL), - speech_synthesis_lib_(NULL), - synaptics_lib_(NULL), - syslogs_lib_(NULL), - system_lib_(NULL), - update_lib_(NULL), - own_library_loader_(true), - own_cryptohome_lib_(true), - own_keyboard_lib_(true), - own_input_method_lib_(true), - own_login_lib_(true), - own_mount_lib_(true), - own_network_lib_(true), - own_power_lib_(true), - own_screen_lock_lib_(true), - own_speech_synthesis_lib_(true), - own_synaptics_lib_(true), - own_syslogs_lib_(true), - own_system_lib_(true), - own_update_lib_(true), + own_library_loader_(false), + use_stub_impl_(false), loaded_(false), load_error_(false), test_api_(NULL) { - } CrosLibrary::~CrosLibrary() { if (own_library_loader_) delete library_loader_; - if (own_cryptohome_lib_) - delete crypto_lib_; - if (own_keyboard_lib_) - delete keyboard_lib_; - if (own_input_method_lib_) - delete input_method_lib_; - if (own_login_lib_) - delete login_lib_; - if (own_mount_lib_) - delete mount_lib_; - if (own_network_lib_) - delete network_lib_; - if (own_power_lib_) - delete power_lib_; - if (own_screen_lock_lib_) - delete screen_lock_lib_; - if (own_speech_synthesis_lib_) - delete speech_synthesis_lib_; - if (own_synaptics_lib_) - delete synaptics_lib_; - if (own_syslogs_lib_) - delete syslogs_lib_; - if (own_system_lib_) - delete system_lib_; - if (own_update_lib_) - delete update_lib_; - delete test_api_; } // static @@ -92,88 +40,71 @@ CrosLibrary* CrosLibrary::Get() { return Singleton<CrosLibrary>::get(); } +BurnLibrary* CrosLibrary::GetBurnLibrary() { + return burn_lib_.GetDefaultImpl(use_stub_impl_); +} + CryptohomeLibrary* CrosLibrary::GetCryptohomeLibrary() { - if (!crypto_lib_) - crypto_lib_ = new CryptohomeLibraryImpl(); - return crypto_lib_; + return crypto_lib_.GetDefaultImpl(use_stub_impl_); } KeyboardLibrary* CrosLibrary::GetKeyboardLibrary() { - if (!keyboard_lib_) - keyboard_lib_ = new KeyboardLibraryImpl(); - return keyboard_lib_; + return keyboard_lib_.GetDefaultImpl(use_stub_impl_); } InputMethodLibrary* CrosLibrary::GetInputMethodLibrary() { - if (!input_method_lib_) - input_method_lib_ = new InputMethodLibraryImpl(); - return input_method_lib_; + return input_method_lib_.GetDefaultImpl(use_stub_impl_); } LoginLibrary* CrosLibrary::GetLoginLibrary() { - if (!login_lib_) - login_lib_ = new LoginLibraryImpl(); - return login_lib_; + return login_lib_.GetDefaultImpl(use_stub_impl_); } MountLibrary* CrosLibrary::GetMountLibrary() { - if (!mount_lib_) - mount_lib_ = new MountLibraryImpl(); - return mount_lib_; + return mount_lib_.GetDefaultImpl(use_stub_impl_); } NetworkLibrary* CrosLibrary::GetNetworkLibrary() { - if (!network_lib_) - network_lib_ = new NetworkLibraryImpl(); - return network_lib_; + return network_lib_.GetDefaultImpl(use_stub_impl_); } PowerLibrary* CrosLibrary::GetPowerLibrary() { - if (!power_lib_) - power_lib_ = new PowerLibraryImpl(); - return power_lib_; + return power_lib_.GetDefaultImpl(use_stub_impl_); } ScreenLockLibrary* CrosLibrary::GetScreenLockLibrary() { - if (!screen_lock_lib_) - screen_lock_lib_ = new ScreenLockLibraryImpl(); - return screen_lock_lib_; + return screen_lock_lib_.GetDefaultImpl(use_stub_impl_); } SpeechSynthesisLibrary* CrosLibrary::GetSpeechSynthesisLibrary() { - if (!speech_synthesis_lib_) - speech_synthesis_lib_ = new SpeechSynthesisLibraryImpl(); - return speech_synthesis_lib_; -} - -SynapticsLibrary* CrosLibrary::GetSynapticsLibrary() { - if (!synaptics_lib_) - synaptics_lib_ = new SynapticsLibraryImpl(); - return synaptics_lib_; + return speech_synthesis_lib_.GetDefaultImpl(use_stub_impl_); } SyslogsLibrary* CrosLibrary::GetSyslogsLibrary() { - if (!syslogs_lib_) - syslogs_lib_ = new SyslogsLibraryImpl(); - return syslogs_lib_; + return syslogs_lib_.GetDefaultImpl(use_stub_impl_); } SystemLibrary* CrosLibrary::GetSystemLibrary() { - if (!system_lib_) - system_lib_ = new SystemLibraryImpl(); - return system_lib_; + return system_lib_.GetDefaultImpl(use_stub_impl_); +} + +TouchpadLibrary* CrosLibrary::GetTouchpadLibrary() { + return touchpad_lib_.GetDefaultImpl(use_stub_impl_); } UpdateLibrary* CrosLibrary::GetUpdateLibrary() { - if (!update_lib_) - update_lib_ = new UpdateLibraryImpl(); - return update_lib_; + return update_lib_.GetDefaultImpl(use_stub_impl_); } bool CrosLibrary::EnsureLoaded() { + if (use_stub_impl_) + return true; + if (!loaded_ && !load_error_) { - if (!library_loader_) + if (!library_loader_) { library_loader_ = new CrosLibraryLoader(); + own_library_loader_ = true; + } loaded_ = library_loader_->Load(&load_error_string_); load_error_ = !loaded_; } @@ -181,9 +112,17 @@ bool CrosLibrary::EnsureLoaded() { } CrosLibrary::TestApi* CrosLibrary::GetTestApi() { - if (!test_api_) - test_api_ = new TestApi(this); - return test_api_; + if (!test_api_.get()) + test_api_.reset(new TestApi(this)); + return test_api_.get(); +} + +void CrosLibrary::TestApi::SetUseStubImpl() { + library_->use_stub_impl_ = true; +} + +void CrosLibrary::TestApi::ResetUseStubImpl() { + library_->use_stub_impl_ = false; } void CrosLibrary::TestApi::SetLibraryLoader(LibraryLoader* loader, bool own) { @@ -199,105 +138,74 @@ void CrosLibrary::TestApi::SetLibraryLoader(LibraryLoader* loader, bool own) { library_->load_error_ = false; } -void CrosLibrary::TestApi::SetCryptohomeLibrary(CryptohomeLibrary* library, - bool own) { - if (library_->own_cryptohome_lib_) - delete library_->crypto_lib_; - library_->own_cryptohome_lib_ = own; - library_->crypto_lib_ = library; +void CrosLibrary::TestApi::SetBurnLibrary( + BurnLibrary* library, bool own) { + library_->burn_lib_.SetImpl(library, own); +} + +void CrosLibrary::TestApi::SetCryptohomeLibrary( + CryptohomeLibrary* library, bool own) { + library_->crypto_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetKeyboardLibrary(KeyboardLibrary* library, - bool own) { - if (library_->own_keyboard_lib_) - delete library_->keyboard_lib_; - library_->own_keyboard_lib_ = own; - library_->keyboard_lib_ = library; +void CrosLibrary::TestApi::SetKeyboardLibrary( + KeyboardLibrary* library, bool own) { + library_->keyboard_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetInputMethodLibrary(InputMethodLibrary* library, - bool own) { - if (library_->own_input_method_lib_) - delete library_->input_method_lib_; - library_->own_input_method_lib_ = own; - library_->input_method_lib_ = library; +void CrosLibrary::TestApi::SetInputMethodLibrary( + InputMethodLibrary* library, bool own) { + library_->input_method_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetLoginLibrary(LoginLibrary* library, bool own) { - if (library_->own_login_lib_) - delete library_->login_lib_; - library_->own_login_lib_ = own; - library_->login_lib_ = library; +void CrosLibrary::TestApi::SetLoginLibrary( + LoginLibrary* library, bool own) { + library_->login_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetMountLibrary(MountLibrary* library, bool own) { - if (library_->own_mount_lib_) - delete library_->mount_lib_; - library_->own_mount_lib_ = own; - library_->mount_lib_ = library; +void CrosLibrary::TestApi::SetMountLibrary( + MountLibrary* library, bool own) { + library_->mount_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetNetworkLibrary(NetworkLibrary* library, - bool own) { - if (library_->own_network_lib_) - delete library_->network_lib_; - library_->own_network_lib_ = own; - library_->network_lib_ = library; +void CrosLibrary::TestApi::SetNetworkLibrary( + NetworkLibrary* library, bool own) { + library_->network_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetPowerLibrary(PowerLibrary* library, bool own) { - if (library_->own_power_lib_) - delete library_->power_lib_; - library_->own_power_lib_ = own; - library_->power_lib_ = library; +void CrosLibrary::TestApi::SetPowerLibrary( + PowerLibrary* library, bool own) { + library_->power_lib_.SetImpl(library, own); } -void CrosLibrary::TestApi::SetScreenLockLibrary(ScreenLockLibrary* library, - bool own) { - if (library_->own_screen_lock_lib_) - delete library_->screen_lock_lib_; - library_->own_screen_lock_lib_ = own; - library_->screen_lock_lib_ = library; +void CrosLibrary::TestApi::SetScreenLockLibrary( + ScreenLockLibrary* library, bool own) { + library_->screen_lock_lib_.SetImpl(library, own); } void CrosLibrary::TestApi::SetSpeechSynthesisLibrary( SpeechSynthesisLibrary* library, bool own) { - if (library_->own_speech_synthesis_lib_) - delete library_->speech_synthesis_lib_; - library_->own_speech_synthesis_lib_ = own; - library_->speech_synthesis_lib_ = library; -} - -void CrosLibrary::TestApi::SetSynapticsLibrary(SynapticsLibrary* library, - bool own) { - if (library_->own_synaptics_lib_) - delete library_->synaptics_lib_; - library_->own_synaptics_lib_ = own; - library_->synaptics_lib_ = library; -} - -void CrosLibrary::TestApi::SetSyslogsLibrary(SyslogsLibrary* library, - bool own) { - if (library_->syslogs_lib_) - delete library_->syslogs_lib_; - library_->own_syslogs_lib_ = own; - library_->syslogs_lib_ = library; -} - -void CrosLibrary::TestApi::SetSystemLibrary(SystemLibrary* library, - bool own) { - if (library_->system_lib_) - delete library_->system_lib_; - library_->own_system_lib_ = own; - library_->system_lib_ = library; -} - -void CrosLibrary::TestApi::SetUpdateLibrary(UpdateLibrary* library, - bool own) { - if (library_->update_lib_) - delete library_->update_lib_; - library_->own_update_lib_ = own; - library_->update_lib_ = library; + library_->speech_synthesis_lib_.SetImpl(library, own); +} + +void CrosLibrary::TestApi::SetTouchpadLibrary( + TouchpadLibrary* library, bool own) { + library_->touchpad_lib_.SetImpl(library, own); +} + +void CrosLibrary::TestApi::SetSyslogsLibrary( + SyslogsLibrary* library, bool own) { + library_->syslogs_lib_.SetImpl(library, own); +} + +void CrosLibrary::TestApi::SetSystemLibrary( + SystemLibrary* library, bool own) { + library_->system_lib_.SetImpl(library, own); +} + +void CrosLibrary::TestApi::SetUpdateLibrary( + UpdateLibrary* library, bool own) { + library_->update_lib_.SetImpl(library, own); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/cros_library.h b/chrome/browser/chromeos/cros/cros_library.h index b08254a..6695acf 100644 --- a/chrome/browser/chromeos/cros/cros_library.h +++ b/chrome/browser/chromeos/cros/cros_library.h @@ -4,13 +4,15 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_CROS_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_CROS_LIBRARY_H_ +#pragma once #include <string> #include "base/basictypes.h" +#include "base/scoped_ptr.h" #include "base/singleton.h" - namespace chromeos { +class BurnLibrary; class CryptohomeLibrary; class KeyboardLibrary; class InputMethodLibrary; @@ -21,9 +23,9 @@ class NetworkLibrary; class PowerLibrary; class ScreenLockLibrary; class SpeechSynthesisLibrary; -class SynapticsLibrary; class SyslogsLibrary; class SystemLibrary; +class TouchpadLibrary; class UpdateLibrary; // This class handles access to sub-parts of ChromeOS library. it provides @@ -31,16 +33,23 @@ class UpdateLibrary; // be mocked for testing. class CrosLibrary { public: - // This class provides access to internal members of CrosLibrary class for // purpose of testing (i.e. replacement of members' implementation with // mock objects). class TestApi { public: + // Use the stub implementations of the library. This is mainly for + // running the chromeos build of chrome on the desktop. + void SetUseStubImpl(); + // Reset the stub implementations of the library, called after + // SetUseStubImp is called. + void ResetUseStubImpl(); // Passing true for own for these setters will cause them to be deleted // when the CrosLibrary is deleted (or other mocks are set). // Setter for LibraryLoader. void SetLibraryLoader(LibraryLoader* loader, bool own); + // Setter for BurnLibrary. + void SetBurnLibrary(BurnLibrary* library, bool own); // Setter for CryptohomeLibrary. void SetCryptohomeLibrary(CryptohomeLibrary* library, bool own); // Setter for KeyboardLibrary @@ -59,12 +68,12 @@ class CrosLibrary { void SetScreenLockLibrary(ScreenLockLibrary* library, bool own); // Setter for SpeechSynthesisLibrary. void SetSpeechSynthesisLibrary(SpeechSynthesisLibrary* library, bool own); - // Setter for SynapticsLibrary. - void SetSynapticsLibrary(SynapticsLibrary* library, bool own); // Setter for SyslogsLibrary. void SetSyslogsLibrary(SyslogsLibrary* library, bool own); // Setter for SystemLibrary. void SetSystemLibrary(SystemLibrary* library, bool own); + // Setter for TouchpadLibrary. + void SetTouchpadLibrary(TouchpadLibrary* library, bool own); // Setter for UpdateLibrary. void SetUpdateLibrary(UpdateLibrary* library, bool own); @@ -77,6 +86,9 @@ class CrosLibrary { // This gets the CrosLibrary. static CrosLibrary* Get(); + // Getter for BurnLibrary. + BurnLibrary* GetBurnLibrary(); + // Getter for CryptohomeLibrary. CryptohomeLibrary* GetCryptohomeLibrary(); @@ -104,15 +116,15 @@ class CrosLibrary { // This gets the singleton SpeechSynthesisLibrary. SpeechSynthesisLibrary* GetSpeechSynthesisLibrary(); - // This gets the singleton SynapticsLibrary. - SynapticsLibrary* GetSynapticsLibrary(); - // This gets the singleton SyslogsLibrary. SyslogsLibrary* GetSyslogsLibrary(); // This gets the singleton SystemLibrary. SystemLibrary* GetSystemLibrary(); + // This gets the singleton TouchpadLibrary. + TouchpadLibrary* GetTouchpadLibrary(); + // This gets the singleton UpdateLibrary. UpdateLibrary* GetUpdateLibrary(); @@ -136,42 +148,67 @@ class CrosLibrary { virtual ~CrosLibrary(); LibraryLoader* library_loader_; - CryptohomeLibrary* crypto_lib_; - KeyboardLibrary* keyboard_lib_; - InputMethodLibrary* input_method_lib_; - LoginLibrary* login_lib_; - MountLibrary* mount_lib_; - NetworkLibrary* network_lib_; - PowerLibrary* power_lib_; - ScreenLockLibrary* screen_lock_lib_; - SpeechSynthesisLibrary* speech_synthesis_lib_; - SynapticsLibrary* synaptics_lib_; - SyslogsLibrary* syslogs_lib_; - SystemLibrary* system_lib_; - UpdateLibrary* update_lib_; bool own_library_loader_; - bool own_cryptohome_lib_; - bool own_keyboard_lib_; - bool own_input_method_lib_; - bool own_login_lib_; - bool own_mount_lib_; - bool own_network_lib_; - bool own_power_lib_; - bool own_screen_lock_lib_; - bool own_speech_synthesis_lib_; - bool own_synaptics_lib_; - bool own_syslogs_lib_; - bool own_system_lib_; - bool own_update_lib_; + // This template supports the creation, setting and optional deletion of + // the cros libraries. + template <class L> + class Library { + public: + Library() : library_(NULL), own_(true) {} + + ~Library() { + if (own_) + delete library_; + } + + L* GetDefaultImpl(bool use_stub_impl) { + if (!library_) { + own_ = true; + library_ = L::GetImpl(use_stub_impl); + } + return library_; + } + + void SetImpl(L* library, bool own) { + if (library != library_) { + if (own_) + delete library_; + library_ = library; + own_ = own; + } + } + + private: + L* library_; + bool own_; + }; + + Library<BurnLibrary> burn_lib_; + Library<CryptohomeLibrary> crypto_lib_; + Library<KeyboardLibrary> keyboard_lib_; + Library<InputMethodLibrary> input_method_lib_; + Library<LoginLibrary> login_lib_; + Library<MountLibrary> mount_lib_; + Library<NetworkLibrary> network_lib_; + Library<PowerLibrary> power_lib_; + Library<ScreenLockLibrary> screen_lock_lib_; + Library<SpeechSynthesisLibrary> speech_synthesis_lib_; + Library<SyslogsLibrary> syslogs_lib_; + Library<SystemLibrary> system_lib_; + Library<TouchpadLibrary> touchpad_lib_; + Library<UpdateLibrary> update_lib_; + + // Stub implementations of the libraries should be used. + bool use_stub_impl_; // True if libcros was successfully loaded. bool loaded_; // True if the last load attempt had an error. bool load_error_; // Contains the error string from the last load attempt. std::string load_error_string_; - TestApi* test_api_; + scoped_ptr<TestApi> test_api_; DISALLOW_COPY_AND_ASSIGN(CrosLibrary); }; diff --git a/chrome/browser/chromeos/cros/cros_library_loader.h b/chrome/browser/chromeos/cros/cros_library_loader.h index c326ab6..3ee1679 100644 --- a/chrome/browser/chromeos/cros/cros_library_loader.h +++ b/chrome/browser/chromeos/cros/cros_library_loader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_CROS_LIBRARY_LOADER_H_ #define CHROME_BROWSER_CHROMEOS_CROS_CROS_LIBRARY_LOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/cros/cryptohome_library.cc b/chrome/browser/chromeos/cros/cryptohome_library.cc index 6ac8432..cd75a85 100644 --- a/chrome/browser/chromeos/cros/cryptohome_library.cc +++ b/chrome/browser/chromeos/cros/cryptohome_library.cc @@ -4,46 +4,239 @@ #include "chrome/browser/chromeos/cros/cryptohome_library.h" +#include "base/hash_tables.h" #include "base/message_loop.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/chromeos/cros/cros_library.h" namespace chromeos { -bool CryptohomeLibraryImpl::CheckKey(const std::string& user_email, - const std::string& passhash) { - return chromeos::CryptohomeCheckKey(user_email.c_str(), passhash.c_str()); -} +// This class handles the interaction with the ChromeOS cryptohome library APIs. +class CryptohomeLibraryImpl : public CryptohomeLibrary { + public: + CryptohomeLibraryImpl() { + if (CrosLibrary::Get()->EnsureLoaded()) + Init(); + } + virtual ~CryptohomeLibraryImpl() {} -bool CryptohomeLibraryImpl::MigrateKey(const std::string& user_email, - const std::string& old_hash, - const std::string& new_hash) { - return chromeos::CryptohomeMigrateKey(user_email.c_str(), - old_hash.c_str(), - new_hash.c_str()); -} + bool CheckKey(const std::string& user_email, const std::string& passhash) { + return chromeos::CryptohomeCheckKey(user_email.c_str(), passhash.c_str()); + } -bool CryptohomeLibraryImpl::Remove(const std::string& user_email) { - return chromeos::CryptohomeRemove(user_email.c_str()); -} + bool AsyncCheckKey(const std::string& user_email, + const std::string& passhash, + Delegate* d) { + return CacheCallback( + chromeos::CryptohomeAsyncCheckKey(user_email.c_str(), passhash.c_str()), + d, + "Couldn't initiate async check of user's key."); + } -bool CryptohomeLibraryImpl::Mount(const std::string& user_email, - const std::string& passhash, - int* error_code) { - return chromeos::CryptohomeMountAllowFail(user_email.c_str(), - passhash.c_str(), - error_code); -} + bool MigrateKey(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash) { + return chromeos::CryptohomeMigrateKey(user_email.c_str(), + old_hash.c_str(), + new_hash.c_str()); + } -bool CryptohomeLibraryImpl::MountForBwsi(int* error_code) { - return chromeos::CryptohomeMountGuest(error_code); -} + bool AsyncMigrateKey(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash, + Delegate* d) { + return CacheCallback( + chromeos::CryptohomeAsyncMigrateKey(user_email.c_str(), + old_hash.c_str(), + new_hash.c_str()), + d, + "Couldn't initiate aync migration of user's key"); + } -bool CryptohomeLibraryImpl::IsMounted() { - return chromeos::CryptohomeIsMounted(); -} + bool Mount(const std::string& user_email, + const std::string& passhash, + int* error_code) { + return chromeos::CryptohomeMountAllowFail(user_email.c_str(), + passhash.c_str(), + error_code); + } + + bool AsyncMount(const std::string& user_email, + const std::string& passhash, + const bool create_if_missing, + Delegate* d) { + return CacheCallback( + chromeos::CryptohomeAsyncMount(user_email.c_str(), + passhash.c_str(), + create_if_missing, + "", + std::vector<std::string>()), + d, + "Couldn't initiate async mount of cryptohome."); + } + + bool MountForBwsi(int* error_code) { + return chromeos::CryptohomeMountGuest(error_code); + } + + bool AsyncMountForBwsi(Delegate* d) { + return CacheCallback(chromeos::CryptohomeAsyncMountGuest(), + d, + "Couldn't initiate async mount of cryptohome."); + } + + bool Remove(const std::string& user_email) { + return chromeos::CryptohomeRemove(user_email.c_str()); + } + + bool AsyncRemove(const std::string& user_email, Delegate* d) { + return CacheCallback( + chromeos::CryptohomeAsyncRemove(user_email.c_str()), + d, + "Couldn't initiate async removal of cryptohome."); + } + + bool IsMounted() { + return chromeos::CryptohomeIsMounted(); + } + + CryptohomeBlob GetSystemSalt() { + return chromeos::CryptohomeGetSystemSalt(); + } + + private: + static void Handler(const chromeos::CryptohomeAsyncCallStatus& event, + void* cryptohome_library) { + CryptohomeLibraryImpl* library = + reinterpret_cast<CryptohomeLibraryImpl*>(cryptohome_library); + library->Dispatch(event); + } + + void Init() { + cryptohome_connection_ = chromeos::CryptohomeMonitorSession(&Handler, this); + } + + void Dispatch(const chromeos::CryptohomeAsyncCallStatus& event) { + callback_map_[event.async_id]->OnComplete(event.return_status, + event.return_code); + callback_map_[event.async_id] = NULL; + } + + bool CacheCallback(int async_id, + Delegate* d, + const char* error) { + if (async_id == 0) { + LOG(ERROR) << error; + return false; + } + callback_map_[async_id] = d; + return true; + } + + typedef base::hash_map<int, Delegate*> CallbackMap; + mutable CallbackMap callback_map_; + + void* cryptohome_connection_; + + DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl); +}; + +class CryptohomeLibraryStubImpl : public CryptohomeLibrary { + public: + CryptohomeLibraryStubImpl() {} + virtual ~CryptohomeLibraryStubImpl() {} + + bool CheckKey(const std::string& user_email, const std::string& passhash) { + return true; + } + + bool AsyncCheckKey(const std::string& user_email, + const std::string& passhash, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + + bool MigrateKey(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash) { + return true; + } + + bool AsyncMigrateKey(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + + bool Remove(const std::string& user_email) { + return true; + } + + bool AsyncRemove(const std::string& user_email, Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + + bool Mount(const std::string& user_email, + const std::string& passhash, + int* error_code) { + return true; + } + + bool AsyncMount(const std::string& user_email, + const std::string& passhash, + const bool create_if_missing, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + + bool MountForBwsi(int* error_code) { + return true; + } + + bool AsyncMountForBwsi(Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + + bool IsMounted() { + return true; + } + + CryptohomeBlob GetSystemSalt() { + CryptohomeBlob salt = CryptohomeBlob(); + salt.push_back(0); + salt.push_back(0); + return salt; + } + + private: + static void DoStubCallback(Delegate* callback) { + callback->OnComplete(true, kCryptohomeMountErrorNone); + } + DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryStubImpl); +}; -CryptohomeBlob CryptohomeLibraryImpl::GetSystemSalt() { - return chromeos::CryptohomeGetSystemSalt(); +// static +CryptohomeLibrary* CryptohomeLibrary::GetImpl(bool stub) { + if (stub) + return new CryptohomeLibraryStubImpl(); + else + return new CryptohomeLibraryImpl(); } -} // namespace chromeos +} // namespace chromeos diff --git a/chrome/browser/chromeos/cros/cryptohome_library.h b/chrome/browser/chromeos/cros/cryptohome_library.h index 6e91d30..9a72761 100644 --- a/chrome/browser/chromeos/cros/cryptohome_library.h +++ b/chrome/browser/chromeos/cros/cryptohome_library.h @@ -4,10 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_CRYPTOHOME_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_CRYPTOHOME_LIBRARY_H_ +#pragma once #include <string> #include "base/singleton.h" +#include "chrome/browser/chromeos/cros/cros_library.h" #include "cros/chromeos_cryptohome.h" namespace chromeos { @@ -16,68 +18,90 @@ namespace chromeos { // APIs. class CryptohomeLibrary { public: - virtual ~CryptohomeLibrary() {} - - // Asks cryptohomed to try to find the cryptohome for |user_email| and then - // mount it using |passhash| to unlock the key. - virtual bool Mount(const std::string& user_email, - const std::string& passhash, - int* error_code) = 0; + class Delegate { + public: + // This will be called back on the UI thread. Consult |return_code| for + // further information beyond mere success or failure. + virtual void OnComplete(bool success, int return_code) = 0; + }; - // Asks cryptohomed to mount a tmpfs for BWSI mode. - virtual bool MountForBwsi(int* error_code) = 0; + virtual ~CryptohomeLibrary() {} // Asks cryptohomed to try to find the cryptohome for |user_email| and then // use |passhash| to unlock the key. virtual bool CheckKey(const std::string& user_email, const std::string& passhash) = 0; + // Asks cryptohomed to asynchronously try to find the cryptohome for + // |user_email| and then use |passhash| to unlock the key. + // Returns true if the attempt is successfully initiated. + // d->OnComplete() will be called with status info on completion. + virtual bool AsyncCheckKey(const std::string& user_email, + const std::string& passhash, + Delegate* callback) = 0; + // Asks cryptohomed to try to find the cryptohome for |user_email| and then // change from using |old_hash| to lock the key to using |new_hash|. virtual bool MigrateKey(const std::string& user_email, const std::string& old_hash, const std::string& new_hash) = 0; - // Asks cryptohomed to try to find the cryptohome for |user_email| and then - // nuke it. - virtual bool Remove(const std::string& user_email) = 0; - - // Asks cryptohomed if a drive is currently mounted. - virtual bool IsMounted() = 0; - - // Asks cryptohomed for the system salt. - virtual CryptohomeBlob GetSystemSalt() = 0; - -}; + // Asks cryptohomed to asynchronously try to find the cryptohome for + // |user_email| and then change from using |old_hash| to lock the + // key to using |new_hash|. + // Returns true if the attempt is successfully initiated. + // d->OnComplete() will be called with status info on completion. + virtual bool AsyncMigrateKey(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash, + Delegate* callback) = 0; -// This class handles the interaction with the ChromeOS cryptohome library APIs. -class CryptohomeLibraryImpl : public CryptohomeLibrary { - public: - CryptohomeLibraryImpl() {} - virtual ~CryptohomeLibraryImpl() {} - - // CryptohomeLibrary overrides. + // Asks cryptohomed to try to find the cryptohome for |user_email| and then + // mount it using |passhash| to unlock the key. virtual bool Mount(const std::string& user_email, const std::string& passhash, - int* error_code); + int* error_code) = 0; - virtual bool MountForBwsi(int* error_code); + // Asks cryptohomed to asynchronously try to find the cryptohome for + // |user_email| and then mount it using |passhash| to unlock the key. + // |create_if_missing| controls whether or not we ask cryptohomed to + // create a new home dir if one does not yet exist for |user_email|. + // Returns true if the attempt is successfully initiated. + // d->OnComplete() will be called with status info on completion. + // If |create_if_missing| is false, and no cryptohome exists for |user_email|, + // we'll get d->OnComplete(false, kCryptohomeMountErrorUserDoesNotExist). + // Otherwise, we expect the normal range of return codes. + virtual bool AsyncMount(const std::string& user_email, + const std::string& passhash, + const bool create_if_missing, + Delegate* callback) = 0; - virtual bool CheckKey(const std::string& user_email, - const std::string& passhash); + // Asks cryptohomed to mount a tmpfs for BWSI mode. + virtual bool MountForBwsi(int* error_code) = 0; - virtual bool MigrateKey(const std::string& user_email, - const std::string& old_hash, - const std::string& new_hash); + // Asks cryptohomed to asynchronously to mount a tmpfs for BWSI mode. + // Returns true if the attempt is successfully initiated. + // d->OnComplete() will be called with status info on completion. + virtual bool AsyncMountForBwsi(Delegate* callback) = 0; + + // Asks cryptohomed to try to find the cryptohome for |user_email| and then + // nuke it. + virtual bool Remove(const std::string& user_email) = 0; - virtual bool Remove(const std::string& user_email); + // Asks cryptohomed to asynchronously try to find the cryptohome for + // |user_email| and then nuke it. + virtual bool AsyncRemove(const std::string& user_email, + Delegate* callback) = 0; - virtual bool IsMounted(); + // Asks cryptohomed if a drive is currently mounted. + virtual bool IsMounted() = 0; - virtual CryptohomeBlob GetSystemSalt(); + // Asks cryptohomed for the system salt. + virtual CryptohomeBlob GetSystemSalt() = 0; - private: - DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static CryptohomeLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/input_method_library.cc b/chrome/browser/chromeos/cros/input_method_library.cc index 260e507..933700b 100644 --- a/chrome/browser/chromeos/cros/input_method_library.cc +++ b/chrome/browser/chromeos/cros/input_method_library.cc @@ -4,20 +4,27 @@ #include "chrome/browser/chromeos/cros/input_method_library.h" +#include <glib.h> +#include <signal.h> + +#include "unicode/uloc.h" + #include "base/basictypes.h" #include "base/message_loop.h" #include "base/string_util.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/keyboard_library.h" +#include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/chromeos/language_preferences.h" -#include "third_party/icu/public/common/unicode/uloc.h" - -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" namespace { +const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; +const char kCandidateWindowPath[] = "/opt/google/chrome/candidate_window"; // Finds a property which has |new_prop.key| from |prop_list|, and replaces the // property with |new_prop|. Returns true if such a property is found. @@ -37,265 +44,737 @@ bool FindAndUpdateProperty(const chromeos::ImeProperty& new_prop, return false; } -// The default keyboard layout. -const char kDefaultKeyboardLayout[] = "us"; - } // namespace namespace chromeos { -InputMethodLibraryImpl::InputMethodLibraryImpl() - : input_method_status_connection_(NULL), - previous_input_method_("", "", "", ""), - current_input_method_("", "", "", "") { - scoped_ptr<InputMethodDescriptors> input_method_descriptors( - CreateFallbackInputMethodDescriptors()); - current_input_method_ = input_method_descriptors->at(0); -} - -InputMethodLibraryImpl::~InputMethodLibraryImpl() { - if (input_method_status_connection_) { - chromeos::DisconnectInputMethodStatus(input_method_status_connection_); +class InputMethodLibraryImpl : public InputMethodLibrary, + public NotificationObserver { + public: + InputMethodLibraryImpl() + : input_method_status_connection_(NULL), + previous_input_method_("", "", "", ""), + current_input_method_("", "", "", ""), + should_launch_ime_(false), + ime_connected_(false), + defer_ime_startup_(false), + should_change_input_method_(false), + ibus_daemon_process_id_(0), + candidate_window_process_id_(0), + failure_count_(0) { + scoped_ptr<InputMethodDescriptors> input_method_descriptors( + CreateFallbackInputMethodDescriptors()); + current_input_method_ = input_method_descriptors->at(0); + if (CrosLibrary::Get()->EnsureLoaded()) { + current_input_method_id_ = chromeos::GetHardwareKeyboardLayoutName(); + } + // Observe APP_EXITING to stop input method processes gracefully. + // Note that even if we fail to stop input method processes from + // Chrome in case of a sudden crash, we have a way to do it from an + // upstart script. See crosbug.com/6515 and crosbug.com/6995 for + // details. + notification_registrar_.Add(this, NotificationType::APP_EXITING, + NotificationService::AllSources()); } -} -InputMethodLibraryImpl::Observer::~Observer() { -} + ~InputMethodLibraryImpl() { + } -void InputMethodLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void InputMethodLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -chromeos::InputMethodDescriptors* -InputMethodLibraryImpl::GetActiveInputMethods() { - chromeos::InputMethodDescriptors* result = NULL; - // The connection does not need to be alive, but it does need to be created. - if (EnsureLoadedAndStarted()) { - result = chromeos::GetActiveInputMethods(input_method_status_connection_); + InputMethodDescriptors* GetActiveInputMethods() { + chromeos::InputMethodDescriptors* result = NULL; + // The connection does not need to be alive, but it does need to be created. + if (EnsureLoadedAndStarted()) { + result = chromeos::GetActiveInputMethods(input_method_status_connection_); + } + if (!result || result->empty()) { + result = CreateFallbackInputMethodDescriptors(); + } + return result; } - if (!result || result->empty()) { - result = CreateFallbackInputMethodDescriptors(); + + size_t GetNumActiveInputMethods() { + scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); + return input_methods->size(); } - return result; -} -size_t InputMethodLibraryImpl::GetNumActiveInputMethods() { - scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); - return input_methods->size(); -} + InputMethodDescriptors* GetSupportedInputMethods() { + InputMethodDescriptors* result = NULL; + // The connection does not need to be alive, but it does need to be created. + if (EnsureLoadedAndStarted()) { + result = chromeos::GetSupportedInputMethods( + input_method_status_connection_); + } + if (!result || result->empty()) { + result = CreateFallbackInputMethodDescriptors(); + } + return result; + } -chromeos::InputMethodDescriptors* -InputMethodLibraryImpl::GetSupportedInputMethods() { - chromeos::InputMethodDescriptors* result = NULL; - // The connection does not need to be alive, but it does need to be created. - if (EnsureLoadedAndStarted()) { - result = chromeos::GetSupportedInputMethods( - input_method_status_connection_); + void ChangeInputMethod(const std::string& input_method_id) { + current_input_method_id_ = input_method_id; + if (EnsureLoadedAndStarted()) { + if (input_method_id != chromeos::GetHardwareKeyboardLayoutName()) { + StartInputMethodProcesses(); + } + chromeos::ChangeInputMethod( + input_method_status_connection_, input_method_id.c_str()); + } } - if (!result || result->empty()) { - result = CreateFallbackInputMethodDescriptors(); + + void SetImePropertyActivated(const std::string& key, bool activated) { + DCHECK(!key.empty()); + if (EnsureLoadedAndStarted()) { + chromeos::SetImePropertyActivated( + input_method_status_connection_, key.c_str(), activated); + } } - return result; -} -void InputMethodLibraryImpl::ChangeInputMethod( - const std::string& input_method_id) { - if (EnsureLoadedAndStarted()) { - chromeos::ChangeInputMethod( - input_method_status_connection_, input_method_id.c_str()); + bool InputMethodIsActivated(const std::string& input_method_id) { + scoped_ptr<InputMethodDescriptors> active_input_method_descriptors( + CrosLibrary::Get()->GetInputMethodLibrary()->GetActiveInputMethods()); + for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) { + if (active_input_method_descriptors->at(i).id == input_method_id) { + return true; + } + } + return false; } -} -void InputMethodLibraryImpl::SetImePropertyActivated(const std::string& key, - bool activated) { - DCHECK(!key.empty()); - if (EnsureLoadedAndStarted()) { - chromeos::SetImePropertyActivated( - input_method_status_connection_, key.c_str(), activated); + bool GetImeConfig(const char* section, const char* config_name, + ImeConfigValue* out_value) { + bool success = false; + if (EnsureLoadedAndStarted()) { + success = chromeos::GetImeConfig(input_method_status_connection_, + section, config_name, out_value); + } + return success; } -} -bool InputMethodLibraryImpl::InputMethodIsActivated( - const std::string& input_method_id) { - scoped_ptr<InputMethodDescriptors> active_input_method_descriptors( - CrosLibrary::Get()->GetInputMethodLibrary()->GetActiveInputMethods()); - for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) { - if (active_input_method_descriptors->at(i).id == input_method_id) { - return true; + bool SetImeConfig(const char* section, const char* config_name, + const ImeConfigValue& value) { + MaybeStartOrStopInputMethodProcesses(section, config_name, value); + + const ConfigKeyType key = std::make_pair(section, config_name); + current_config_values_[key] = value; + if (ime_connected_) { + pending_config_requests_[key] = value; + FlushImeConfig(); } + return pending_config_requests_.empty(); } - return false; -} -bool InputMethodLibraryImpl::GetImeConfig( - const char* section, const char* config_name, ImeConfigValue* out_value) { - bool success = false; - if (EnsureLoadedAndStarted()) { - success = chromeos::GetImeConfig( - input_method_status_connection_, section, config_name, out_value); + virtual const InputMethodDescriptor& previous_input_method() const { + return previous_input_method_; + } + virtual const InputMethodDescriptor& current_input_method() const { + return current_input_method_; } - return success; -} -bool InputMethodLibraryImpl::SetImeConfig( - const char* section, const char* config_name, const ImeConfigValue& value) { - const ConfigKeyType key = std::make_pair(section, config_name); - pending_config_requests_.erase(key); - pending_config_requests_.insert(std::make_pair(key, value)); - current_config_values_[key] = value; - FlushImeConfig(); - return pending_config_requests_.empty(); -} + virtual const ImePropertyList& current_ime_properties() const { + return current_ime_properties_; + } -void InputMethodLibraryImpl::FlushImeConfig() { - bool active_input_methods_are_changed = false; - if (EnsureLoadedAndStarted()) { - InputMethodConfigRequests::iterator iter = pending_config_requests_.begin(); - while (iter != pending_config_requests_.end()) { - const std::string& section = iter->first.first; - const std::string& config_name = iter->first.second; - const ImeConfigValue& value = iter->second; - if (chromeos::SetImeConfig(input_method_status_connection_, - section.c_str(), config_name.c_str(), value)) { - // Successfully sent. Remove the command and proceed to the next one. - pending_config_requests_.erase(iter++); - // Check if it's a change in active input methods. - if (config_name == kPreloadEnginesConfigName) { - active_input_methods_are_changed = true; + private: + // Starts or stops the input method processes based on the current state. + void MaybeStartOrStopInputMethodProcesses( + const char* section, + const char* config_name, + const ImeConfigValue& value) { + if (!strcmp(language_prefs::kGeneralSectionName, section) && + !strcmp(language_prefs::kPreloadEnginesConfigName, config_name)) { + if (EnsureLoadedAndStarted()) { + // If there are no input methods other than one for the hardware + // keyboard, we'll stop the input method processes. + if (value.type == ImeConfigValue::kValueTypeStringList && + value.string_list_value.size() == 1 && + value.string_list_value[0] == + chromeos::GetHardwareKeyboardLayoutName()) { + StopInputMethodProcesses(); + } else if (!defer_ime_startup_) { + StartInputMethodProcesses(); + } + chromeos::SetActiveInputMethods(input_method_status_connection_, value); + } + } + } + + // Flushes the input method config data. The config data is queued up in + // |pending_config_requests_| until the config backend (ibus-memconf) + // starts. Since there is no good way to get notified when the config + // backend starts, we use a timer to periodically attempt to send the + // config data to the config backend. + void FlushImeConfig() { + bool active_input_methods_are_changed = false; + bool completed = false; + if (EnsureLoadedAndStarted()) { + InputMethodConfigRequests::iterator iter = + pending_config_requests_.begin(); + while (iter != pending_config_requests_.end()) { + const std::string& section = iter->first.first; + const std::string& config_name = iter->first.second; + const ImeConfigValue& value = iter->second; + if (chromeos::SetImeConfig(input_method_status_connection_, + section.c_str(), + config_name.c_str(), + value)) { + // Check if it's a change in active input methods. + if (config_name == language_prefs::kPreloadEnginesConfigName) { + active_input_methods_are_changed = true; + } + // Successfully sent. Remove the command and proceed to the next one. + pending_config_requests_.erase(iter++); + } else { + // If SetImeConfig() fails, subsequent calls will likely fail. + break; + } + } + if (pending_config_requests_.empty()) { + // Calls to ChangeInputMethod() will fail if the input method has not + // yet been added to preload_engines. As such, the call is deferred + // until after all config values have been sent to the IME process. + if (should_change_input_method_) { + if (chromeos::ChangeInputMethod(input_method_status_connection_, + current_input_method_id_.c_str())) { + should_change_input_method_ = false; + completed = true; + active_input_methods_are_changed = true; + } + } else { + completed = true; } - } else { - LOG(ERROR) << "chromeos::SetImeConfig failed. Will retry later: " - << section << "/" << config_name; - ++iter; // Do not remove the command. } } - if (pending_config_requests_.empty()) { + + if (completed) { timer_.Stop(); // no-op if it's not running. + } else { + // Flush is not completed. Start a timer if it's not yet running. + if (!timer_.IsRunning()) { + static const int64 kTimerIntervalInMsec = 100; + failure_count_ = 0; + timer_.Start(base::TimeDelta::FromMilliseconds(kTimerIntervalInMsec), + this, &InputMethodLibraryImpl::FlushImeConfig); + } else { + // The timer is already running. We'll give up if it reaches the + // max retry count. + static const int kMaxRetries = 15; + ++failure_count_; + if (failure_count_ > kMaxRetries) { + LOG(ERROR) << "FlushImeConfig: Max retries exceeded. " + << "current_input_method_id: " << current_input_method_id_ + << " pending_config_requests.size: " + << pending_config_requests_.size(); + timer_.Stop(); + } + } } - } else { - if (!timer_.IsRunning()) { - static const int64 kTimerIntervalInSec = 1; - timer_.Start(base::TimeDelta::FromSeconds(kTimerIntervalInSec), this, - &InputMethodLibraryImpl::FlushImeConfig); + if (active_input_methods_are_changed) { + FOR_EACH_OBSERVER(Observer, observers_, ActiveInputMethodsChanged(this)); } } - if (active_input_methods_are_changed) { - FOR_EACH_OBSERVER(Observer, observers_, ActiveInputMethodsChanged(this)); + + static void InputMethodChangedHandler( + void* object, + const chromeos::InputMethodDescriptor& current_input_method) { + // The handler is called when the input method method change is + // notified via a DBus connection. Since the DBus notificatiosn are + // handled in the UI thread, we can assume that this functionalways + // runs on the UI thread, but just in case. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + LOG(ERROR) << "Not on UI thread"; + return; + } + + InputMethodLibraryImpl* input_method_library = + static_cast<InputMethodLibraryImpl*>(object); + input_method_library->ChangeCurrentInputMethod(current_input_method); } -} -// static -void InputMethodLibraryImpl::InputMethodChangedHandler( - void* object, const chromeos::InputMethodDescriptor& current_input_method) { - InputMethodLibraryImpl* input_method_library = - static_cast<InputMethodLibraryImpl*>(object); - input_method_library->UpdateCurrentInputMethod(current_input_method); -} + static void RegisterPropertiesHandler( + void* object, const ImePropertyList& prop_list) { + // See comments in InputMethodChangedHandler. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + LOG(ERROR) << "Not on UI thread"; + return; + } -// static -void InputMethodLibraryImpl::RegisterPropertiesHandler( - void* object, const ImePropertyList& prop_list) { - InputMethodLibraryImpl* input_method_library = - static_cast<InputMethodLibraryImpl*>(object); - input_method_library->RegisterProperties(prop_list); -} + InputMethodLibraryImpl* input_method_library = + static_cast<InputMethodLibraryImpl*>(object); + input_method_library->RegisterProperties(prop_list); + } -// static -void InputMethodLibraryImpl::UpdatePropertyHandler( - void* object, const ImePropertyList& prop_list) { - InputMethodLibraryImpl* input_method_library = - static_cast<InputMethodLibraryImpl*>(object); - input_method_library->UpdateProperty(prop_list); -} + static void UpdatePropertyHandler( + void* object, const ImePropertyList& prop_list) { + // See comments in InputMethodChangedHandler. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + LOG(ERROR) << "Not on UI thread"; + return; + } -// static -void InputMethodLibraryImpl::ConnectionChangeHandler(void* object, - bool connected) { - InputMethodLibraryImpl* input_method_library = - static_cast<InputMethodLibraryImpl*>(object); - if (connected) { - input_method_library->pending_config_requests_.insert( - input_method_library->current_config_values_.begin(), - input_method_library->current_config_values_.end()); - input_method_library->FlushImeConfig(); + InputMethodLibraryImpl* input_method_library = + static_cast<InputMethodLibraryImpl*>(object); + input_method_library->UpdateProperty(prop_list); } -} -bool InputMethodLibraryImpl::EnsureStarted() { - if (!input_method_status_connection_) { - input_method_status_connection_ = chromeos::MonitorInputMethodStatus( - this, - &InputMethodChangedHandler, - &RegisterPropertiesHandler, - &UpdatePropertyHandler, - &ConnectionChangeHandler); + static void ConnectionChangeHandler(void* object, bool connected) { + // See comments in InputMethodChangedHandler. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + LOG(ERROR) << "Not on UI thread"; + return; + } + + InputMethodLibraryImpl* input_method_library = + static_cast<InputMethodLibraryImpl*>(object); + input_method_library->ime_connected_ = connected; + if (connected) { + input_method_library->pending_config_requests_.clear(); + input_method_library->pending_config_requests_.insert( + input_method_library->current_config_values_.begin(), + input_method_library->current_config_values_.end()); + input_method_library->should_change_input_method_ = true; + input_method_library->FlushImeConfig(); + } else { + // Stop attempting to resend config data, since it will continue to fail. + input_method_library->timer_.Stop(); // no-op if it's not running. + } } - return true; -} -bool InputMethodLibraryImpl::EnsureLoadedAndStarted() { - return CrosLibrary::Get()->EnsureLoaded() && - EnsureStarted(); -} + bool EnsureStarted() { + if (!input_method_status_connection_) { + input_method_status_connection_ = chromeos::MonitorInputMethodStatus( + this, + &InputMethodChangedHandler, + &RegisterPropertiesHandler, + &UpdatePropertyHandler, + &ConnectionChangeHandler); + } + return true; + } -void InputMethodLibraryImpl::UpdateCurrentInputMethod( - const chromeos::InputMethodDescriptor& new_input_method) { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - DLOG(INFO) << "UpdateCurrentInputMethod (Background thread)"; - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - // NewRunnableMethod() copies |new_input_method| by value. - NewRunnableMethod( - this, &InputMethodLibraryImpl::UpdateCurrentInputMethod, - new_input_method)); - return; - } - - DLOG(INFO) << "UpdateCurrentInputMethod (UI thread)"; - // Change the keyboard layout to a preferred layout for the input method. - CrosLibrary::Get()->GetKeyboardLibrary()->SetCurrentKeyboardLayoutByName( - new_input_method.keyboard_layout); - - if (current_input_method_.id != new_input_method.id) { - previous_input_method_ = current_input_method_; - current_input_method_ = new_input_method; - } - FOR_EACH_OBSERVER(Observer, observers_, InputMethodChanged(this)); -} + bool EnsureLoadedAndStarted() { + return CrosLibrary::Get()->EnsureLoaded() && + EnsureStarted(); + } + + void ChangeCurrentInputMethod(const InputMethodDescriptor& new_input_method) { + // Change the keyboard layout to a preferred layout for the input method. + CrosLibrary::Get()->GetKeyboardLibrary()->SetCurrentKeyboardLayoutByName( + new_input_method.keyboard_layout); -void InputMethodLibraryImpl::RegisterProperties( - const ImePropertyList& prop_list) { - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod( - this, &InputMethodLibraryImpl::RegisterProperties, prop_list)); - return; + if (current_input_method_.id != new_input_method.id) { + previous_input_method_ = current_input_method_; + current_input_method_ = new_input_method; + } + FOR_EACH_OBSERVER(Observer, observers_, InputMethodChanged(this)); } - // |prop_list| might be empty. This means "clear all properties." - current_ime_properties_ = prop_list; - FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); -} + void RegisterProperties(const ImePropertyList& prop_list) { + // |prop_list| might be empty. This means "clear all properties." + current_ime_properties_ = prop_list; + FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); + } + + void StartInputMethodProcesses() { + should_launch_ime_ = true; + MaybeLaunchInputMethodProcesses(); + } + + void UpdateProperty(const ImePropertyList& prop_list) { + for (size_t i = 0; i < prop_list.size(); ++i) { + FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); + } + FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); + } + + // Launches an input method procsess specified by the given command + // line. On success, returns true and stores the process ID in + // |process_id|. Otherwise, returns false, and the contents of + // |process_id| is untouched. OnImeShutdown will be called when the + // process terminates. + bool LaunchInputMethodProcess(const std::string& command_line, + int* process_id) { + GError *error = NULL; + gchar **argv = NULL; + gint argc = NULL; + // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so" + if (!g_shell_parse_argv(command_line.c_str(), &argc, &argv, &error)) { + LOG(ERROR) << "Could not parse command: " << error->message; + g_error_free(error); + return false; + } + + int pid = 0; + const GSpawnFlags kFlags = G_SPAWN_DO_NOT_REAP_CHILD; + const gboolean result = g_spawn_async(NULL, argv, NULL, + kFlags, NULL, NULL, + &pid, &error); + g_strfreev(argv); + if (!result) { + LOG(ERROR) << "Could not launch: " << command_line << ": " + << error->message; + g_error_free(error); + return false; + } + g_child_watch_add(pid, reinterpret_cast<GChildWatchFunc>(OnImeShutdown), + this); + + *process_id = pid; + return true; + } + + // Launches input method processes if these are not yet running. + void MaybeLaunchInputMethodProcesses() { + if (!should_launch_ime_) { + return; + } + + if (ibus_daemon_process_id_ == 0) { + // TODO(zork): Send output to /var/log/ibus.log + const std::string ibus_daemon_command_line = + StringPrintf("%s --panel=disable --cache=none --restart --replace", + kIBusDaemonPath); + if (!LaunchInputMethodProcess(ibus_daemon_command_line, + &ibus_daemon_process_id_)) { + // On failure, we should not attempt to launch candidate_window. + return; + } + } + + if (candidate_window_process_id_ == 0) { + // Pass the UI language info to candidate_window via --lang flag. + const std::string candidate_window_command_line = + StringPrintf("%s --lang=%s", kCandidateWindowPath, + g_browser_process->GetApplicationLocale().c_str()); + if (!LaunchInputMethodProcess(candidate_window_command_line, + &candidate_window_process_id_)) { + // Return here just in case we add more code below. + return; + } + } + } + + static void OnImeShutdown(int pid, + int status, + InputMethodLibraryImpl* library) { + g_spawn_close_pid(pid); + if (library->ibus_daemon_process_id_ == pid) { + library->ibus_daemon_process_id_ = 0; + } else if (library->candidate_window_process_id_ == pid) { + library->candidate_window_process_id_ = 0; + } + + // Restart input method processes if needed. + library->MaybeLaunchInputMethodProcesses(); + } + + void StopInputMethodProcesses() { + should_launch_ime_ = false; + if (ibus_daemon_process_id_) { + const std::string xkb_engine_name = + chromeos::GetHardwareKeyboardLayoutName(); + // We should not use chromeos::ChangeInputMethod() here since without the + // ibus-daemon process, ChangeCurrentInputMethod() callback function which + // actually changes the XKB layout will not be called. + CrosLibrary::Get()->GetKeyboardLibrary()->SetCurrentKeyboardLayoutByName( + chromeos::input_method::GetKeyboardLayoutName(xkb_engine_name)); + kill(ibus_daemon_process_id_, SIGTERM); + ibus_daemon_process_id_ = 0; + } + if (candidate_window_process_id_) { + kill(candidate_window_process_id_, SIGTERM); + candidate_window_process_id_ = 0; + } + } + + void SetDeferImeStartup(bool defer) { + LOG(INFO) << "Setting DeferImeStartup to " << defer; + defer_ime_startup_ = defer; + } + + // NotificationObserver implementation: + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + // Stop the input processes on browser shutdown. + if (type.value == NotificationType::APP_EXITING) { + StopInputMethodProcesses(); + } + } + + // A reference to the language api, to allow callbacks when the input method + // status changes. + InputMethodStatusConnection* input_method_status_connection_; + ObserverList<Observer> observers_; + + // The input method which was/is selected. + InputMethodDescriptor previous_input_method_; + InputMethodDescriptor current_input_method_; + + // The input method properties which the current input method uses. The list + // might be empty when no input method is used. + ImePropertyList current_ime_properties_; + + typedef std::pair<std::string, std::string> ConfigKeyType; + typedef std::map<ConfigKeyType, ImeConfigValue> InputMethodConfigRequests; + // SetImeConfig requests that are not yet completed. + // Use a map to queue config requests, so we only send the last request for + // the same config key (i.e. we'll discard ealier requests for the same + // config key). As we discard old requests for the same config key, the order + // of requests doesn't matter, so it's safe to use a map. + InputMethodConfigRequests pending_config_requests_; + + // Values that have been set via SetImeConfig(). We keep a copy available to + // resend if the ime restarts and loses its state. + InputMethodConfigRequests current_config_values_; + + // A timer for retrying to send |pendning_config_commands_| to the input + // method config daemon. + base::OneShotTimer<InputMethodLibraryImpl> timer_; + + // This is used to register this object to APP_EXITING notification. + NotificationRegistrar notification_registrar_; + + // True if we should launch the input method processes. + bool should_launch_ime_; + // True if the connection to the IBus daemon is alive. + bool ime_connected_; + // If true, we'll defer the startup until a non-default method is + // activated. + bool defer_ime_startup_; + // The ID of the current input method (ex. "mozc"). + std::string current_input_method_id_; + // True if we should change the input method once the queue of the + // pending config requests becomes empty. + bool should_change_input_method_; + + // The process id of the IBus daemon. 0 if it's not running. The process + // ID 0 is not used in Linux, hence it's safe to use 0 for this purpose. + int ibus_daemon_process_id_; + // The process id of the candidate window. 0 if it's not running. + int candidate_window_process_id_; + // The failure count of config flush attempts. + int failure_count_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryImpl); +}; + +InputMethodLibraryImpl::Observer::~Observer() {} + +class InputMethodLibraryStubImpl : public InputMethodLibrary { + public: + InputMethodLibraryStubImpl() + : previous_input_method_("", "", "", ""), + current_input_method_("", "", "", "") { + } + + ~InputMethodLibraryStubImpl() {} + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + + InputMethodDescriptors* GetActiveInputMethods() { + return CreateRealisticInputMethodDescriptors(); + } + + + size_t GetNumActiveInputMethods() { + scoped_ptr<InputMethodDescriptors> descriptors( + CreateRealisticInputMethodDescriptors()); + return descriptors->size(); + } + + InputMethodDescriptors* GetSupportedInputMethods() { + return CreateRealisticInputMethodDescriptors(); + } + + void ChangeInputMethod(const std::string& input_method_id) {} + void SetImePropertyActivated(const std::string& key, bool activated) {} -void InputMethodLibraryImpl::UpdateProperty(const ImePropertyList& prop_list) { - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod( - this, &InputMethodLibraryImpl::UpdateProperty, prop_list)); - return; + bool InputMethodIsActivated(const std::string& input_method_id) { + return true; } - for (size_t i = 0; i < prop_list.size(); ++i) { - FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); + bool GetImeConfig(const char* section, + const char* config_name, + ImeConfigValue* out_value) { + return false; } - FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); + + bool SetImeConfig(const char* section, + const char* config_name, + const ImeConfigValue& value) { + return false; + } + + virtual const InputMethodDescriptor& previous_input_method() const { + return previous_input_method_; + } + + virtual const InputMethodDescriptor& current_input_method() const { + return current_input_method_; + } + + virtual const ImePropertyList& current_ime_properties() const { + return current_ime_properties_; + } + + virtual void StartInputMethodProcesses() {} + virtual void StopInputMethodProcesses() {} + virtual void SetDeferImeStartup(bool defer) {} + + private: + // Creates realistic input method descriptors that can be used for + // testing Chrome OS version of chrome on regular Linux desktops. + InputMethodDescriptors* CreateRealisticInputMethodDescriptors() { + InputMethodDescriptors* descriptions = new InputMethodDescriptors; + // The list is created from output of gen_engines.py in libcros. + descriptions->push_back(InputMethodDescriptor( + "chewing", "Chewing", "us", "zh_TW")); + descriptions->push_back(InputMethodDescriptor( + "hangul", "Korean", "us", "ko")); + descriptions->push_back(InputMethodDescriptor( + "m17n:fa:isiri", "isiri (m17n)", "us", "fa")); + descriptions->push_back(InputMethodDescriptor( + "m17n:he:kbd", "kbd (m17n)", "us", "he")); + descriptions->push_back(InputMethodDescriptor( + "m17n:ar:kbd", "kbd (m17n)", "us", "ar")); + descriptions->push_back(InputMethodDescriptor( + "m17n:hi:itrans", "itrans (m17n)", "us", "hi")); + descriptions->push_back(InputMethodDescriptor( + "m17n:vi:vni", "vni (m17n)", "us", "vi")); + descriptions->push_back(InputMethodDescriptor( + "m17n:vi:viqr", "viqr (m17n)", "us", "vi")); + descriptions->push_back(InputMethodDescriptor( + "m17n:vi:tcvn", "tcvn (m17n)", "us", "vi")); + descriptions->push_back(InputMethodDescriptor( + "m17n:vi:telex", "telex (m17n)", "us", "vi")); + descriptions->push_back(InputMethodDescriptor( + "m17n:zh:cangjie", "cangjie (m17n)", "us", "zh")); + descriptions->push_back(InputMethodDescriptor( + "m17n:zh:quick", "quick (m17n)", "us", "zh")); + descriptions->push_back(InputMethodDescriptor( + "m17n:th:tis820", "tis820 (m17n)", "us", "th")); + descriptions->push_back(InputMethodDescriptor( + "m17n:th:kesmanee", "kesmanee (m17n)", "us", "th")); + descriptions->push_back(InputMethodDescriptor( + "m17n:th:pattachote", "pattachote (m17n)", "us", "th")); + descriptions->push_back(InputMethodDescriptor( + "mozc-jp", "Mozc (Japanese keyboard layout)", "jp", "ja")); + descriptions->push_back(InputMethodDescriptor( + "mozc", "Mozc (US keyboard layout)", "us", "ja")); + descriptions->push_back(InputMethodDescriptor( + "mozc-dv", "Mozc (US Dvorak keyboard layout)", "us(dvorak)", "ja")); + descriptions->push_back(InputMethodDescriptor( + "pinyin", "Pinyin", "us", "zh")); + descriptions->push_back(InputMethodDescriptor( + "bopomofo", "Bopomofo", "us", "zh")); + descriptions->push_back(InputMethodDescriptor( + "xkb:us::eng", "USA", "us", "eng")); + descriptions->push_back(InputMethodDescriptor( + "xkb:us:dvorak:eng", "USA - Dvorak", "us(dvorak)", "eng")); + descriptions->push_back(InputMethodDescriptor( + "xkb:be::ger", "Belgium", "be", "ger")); + descriptions->push_back(InputMethodDescriptor( + "xkb:be::nld", "Belgium", "be", "nld")); + descriptions->push_back(InputMethodDescriptor( + "xkb:be::fra", "Belgium", "be", "fra")); + descriptions->push_back(InputMethodDescriptor( + "xkb:br::por", "Brazil", "br", "por")); + descriptions->push_back(InputMethodDescriptor( + "xkb:bg::bul", "Bulgaria", "bg", "bul")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ca::fra", "Canada", "ca", "fra")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ca:eng:eng", "Canada - English", "ca(eng)", "eng")); + descriptions->push_back(InputMethodDescriptor( + "xkb:hr::scr", "Croatia", "hr", "scr")); + descriptions->push_back(InputMethodDescriptor( + "xkb:cz::cze", "Czechia", "cz", "cze")); + descriptions->push_back(InputMethodDescriptor( + "xkb:dk::dan", "Denmark", "dk", "dan")); + descriptions->push_back(InputMethodDescriptor( + "xkb:nl::nld", "Netherlands", "nl", "nld")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ee::est", "Estonia", "ee", "est")); + descriptions->push_back(InputMethodDescriptor( + "xkb:fi::fin", "Finland", "fi", "fin")); + descriptions->push_back(InputMethodDescriptor( + "xkb:fr::fra", "France", "fr", "fra")); + descriptions->push_back(InputMethodDescriptor( + "xkb:de::ger", "Germany", "de", "ger")); + descriptions->push_back(InputMethodDescriptor( + "xkb:gr::gre", "Greece", "gr", "gre")); + descriptions->push_back(InputMethodDescriptor( + "xkb:hu::hun", "Hungary", "hu", "hun")); + descriptions->push_back(InputMethodDescriptor( + "xkb:it::ita", "Italy", "it", "ita")); + descriptions->push_back(InputMethodDescriptor( + "xkb:jp::jpn", "Japan", "jp", "jpn")); + descriptions->push_back(InputMethodDescriptor( + "xkb:lt::lit", "Lithuania", "lt", "lit")); + descriptions->push_back(InputMethodDescriptor( + "xkb:lv::lav", "Latvia", "lv", "lav")); + descriptions->push_back(InputMethodDescriptor( + "xkb:no::nor", "Norway", "no", "nor")); + descriptions->push_back(InputMethodDescriptor( + "xkb:pl::pol", "Poland", "pl", "pol")); + descriptions->push_back(InputMethodDescriptor( + "xkb:pt::por", "Portugal", "pt", "por")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ro::rum", "Romania", "ro", "rum")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ru::rus", "Russia", "ru", "rus")); + descriptions->push_back(InputMethodDescriptor( + "xkb:rs::srp", "Serbia", "rs", "srp")); + descriptions->push_back(InputMethodDescriptor( + "xkb:si::slv", "Slovenia", "si", "slv")); + descriptions->push_back(InputMethodDescriptor( + "xkb:sk::slo", "Slovakia", "sk", "slo")); + descriptions->push_back(InputMethodDescriptor( + "xkb:es::spa", "Spain", "es", "spa")); + descriptions->push_back(InputMethodDescriptor( + "xkb:es:cat:cat", + "Spain - Catalan variant with middle-dot L", "es(cat)", "cat")); + descriptions->push_back(InputMethodDescriptor( + "xkb:se::swe", "Sweden", "se", "swe")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ch::ger", "Switzerland", "ch", "ger")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ch:fr:fra", "Switzerland - French", "ch(fr)", "fra")); + descriptions->push_back(InputMethodDescriptor( + "xkb:tr::tur", "Turkey", "tr", "tur")); + descriptions->push_back(InputMethodDescriptor( + "xkb:ua::ukr", "Ukraine", "ua", "ukr")); + descriptions->push_back(InputMethodDescriptor( + "xkb:gb:extd:eng", "United Kingdom - Extended - Winkeys", "gb(extd)", + "eng")); + return descriptions; + } + + InputMethodDescriptor previous_input_method_; + InputMethodDescriptor current_input_method_; + ImePropertyList current_ime_properties_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryStubImpl); +}; + +// static +InputMethodLibrary* InputMethodLibrary::GetImpl(bool stub) { + if (stub) + return new InputMethodLibraryStubImpl(); + else + return new InputMethodLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); diff --git a/chrome/browser/chromeos/cros/input_method_library.h b/chrome/browser/chromeos/cros/input_method_library.h index e92b311..2ae4b85 100644 --- a/chrome/browser/chromeos/cros/input_method_library.h +++ b/chrome/browser/chromeos/cros/input_method_library.h @@ -4,8 +4,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_INPUT_METHOD_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_INPUT_METHOD_LIBRARY_H_ +#pragma once -#include <map> #include <string> #include <utility> @@ -18,18 +18,27 @@ namespace chromeos { // This class handles the interaction with the ChromeOS language library APIs. // Classes can add themselves as observers. Users can get an instance of this -// library class like this: InputMethodLibrary::Get() +// library class like this: +// chromeos::CrosLibrary::Get()->GetInputMethodLibrary() class InputMethodLibrary { public: class Observer { public: virtual ~Observer() = 0; + // Called when the current input method is changed. virtual void InputMethodChanged(InputMethodLibrary* obj) = 0; + + // Called when input method properties (see chromeos_input_method.h + // for details) are changed. virtual void ImePropertiesChanged(InputMethodLibrary* obj) = 0; + + // Called when the active input methods are changed. virtual void ActiveInputMethodsChanged(InputMethodLibrary* obj) = 0; }; virtual ~InputMethodLibrary() {} + // Adds an observer to receive notifications of input method related + // changes as desribed in the Observer class above. virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; @@ -76,124 +85,31 @@ class InputMethodLibrary { // the request and returns false. // You can specify |section| and |config_name| arguments in the same way // as GetImeConfig() above. + // Notice: This function might call the Observer::ActiveInputMethodsChanged() + // callback function immediately, before returning from the SetImeConfig + // function. See also http://crosbug.com/5217. virtual bool SetImeConfig(const char* section, const char* config_name, const ImeConfigValue& value) = 0; + // Sets the IME state to enabled, and launches its processes if needed. + virtual void StartInputMethodProcesses() = 0; + + // Disables the IME, and kills the processes if they are running. + virtual void StopInputMethodProcesses() = 0; + + // Controls whether the IME process is started when preload engines are + // specificed, or defered until a non-default method is activated. + virtual void SetDeferImeStartup(bool defer) = 0; + virtual const InputMethodDescriptor& previous_input_method() const = 0; virtual const InputMethodDescriptor& current_input_method() const = 0; virtual const ImePropertyList& current_ime_properties() const = 0; -}; -// This class handles the interaction with the ChromeOS language library APIs. -// Classes can add themselves as observers. Users can get an instance of this -// library class like this: InputMethodLibrary::Get() -class InputMethodLibraryImpl : public InputMethodLibrary { - public: - InputMethodLibraryImpl(); - virtual ~InputMethodLibraryImpl(); - - // InputMethodLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - virtual InputMethodDescriptors* GetActiveInputMethods(); - virtual size_t GetNumActiveInputMethods(); - virtual InputMethodDescriptors* GetSupportedInputMethods(); - virtual void ChangeInputMethod(const std::string& input_method_id); - virtual void SetImePropertyActivated(const std::string& key, - bool activated); - virtual bool InputMethodIsActivated(const std::string& input_method_id); - virtual bool GetImeConfig( - const char* section, const char* config_name, ImeConfigValue* out_value); - virtual bool SetImeConfig(const char* section, - const char* config_name, - const ImeConfigValue& value); - - virtual const InputMethodDescriptor& previous_input_method() const { - return previous_input_method_; - } - virtual const InputMethodDescriptor& current_input_method() const { - return current_input_method_; - } - - virtual const ImePropertyList& current_ime_properties() const { - return current_ime_properties_; - } - - private: - // This method is called when there's a change in input method status. - static void InputMethodChangedHandler( - void* object, const InputMethodDescriptor& current_input_method); - - // This method is called when an input method sends "RegisterProperties" - // signal. - static void RegisterPropertiesHandler( - void* object, const ImePropertyList& prop_list); - - // This method is called when an input method sends "UpdateProperty" signal. - static void UpdatePropertyHandler( - void* object, const ImePropertyList& prop_list); - - // This method is called when bus connects or disconnects. - static void ConnectionChangeHandler(void* object, bool connected); - - // Ensures that the monitoring of input method changes is started. Starts - // the monitoring if necessary. Returns true if the monitoring has been - // successfully started. - bool EnsureStarted(); - - // Ensures that the cros library is loaded and the the monitoring is - // started. Loads the cros library and starts the monitoring if - // necessary. Returns true if the two conditions are both met. - bool EnsureLoadedAndStarted(); - - // Called by the handler to update the input method status. - // This will notify all the Observers. - void UpdateCurrentInputMethod( - const InputMethodDescriptor& current_input_method); - - // Called by the handler to register input method properties. - void RegisterProperties(const ImePropertyList& prop_list); - - // Called by the handler to update input method properties. - void UpdateProperty(const ImePropertyList& prop_list); - - // Tries to send all pending SetImeConfig requests to the input method config - // daemon. - void FlushImeConfig(); - - // A reference to the language api, to allow callbacks when the input method - // status changes. - InputMethodStatusConnection* input_method_status_connection_; - ObserverList<Observer> observers_; - - // The input method which was/is selected. - InputMethodDescriptor previous_input_method_; - InputMethodDescriptor current_input_method_; - - // The input method properties which the current input method uses. The list - // might be empty when no input method is used. - ImePropertyList current_ime_properties_; - - typedef std::pair<std::string, std::string> ConfigKeyType; - typedef std::map<ConfigKeyType, ImeConfigValue> InputMethodConfigRequests; - // SetImeConfig requests that are not yet completed. - // Use a map to queue config requests, so we only send the last request for - // the same config key (i.e. we'll discard ealier requests for the same - // config key). As we discard old requests for the same config key, the order - // of requests doesn't matter, so it's safe to use a map. - InputMethodConfigRequests pending_config_requests_; - - // Values that have been set via SetImeConfig(). We keep a copy available to - // resend if the ime restarts and loses its state. - InputMethodConfigRequests current_config_values_; - - // A timer for retrying to send |pendning_config_commands_| to the input - // method config daemon. - base::OneShotTimer<InputMethodLibraryImpl> timer_; - - DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static InputMethodLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/keyboard_library.cc b/chrome/browser/chromeos/cros/keyboard_library.cc index ff7a3b1..c7728c1 100644 --- a/chrome/browser/chromeos/cros/keyboard_library.cc +++ b/chrome/browser/chromeos/cros/keyboard_library.cc @@ -9,34 +9,140 @@ namespace chromeos { -std::string KeyboardLibraryImpl::GetCurrentKeyboardLayoutName() const { - if (CrosLibrary::Get()->EnsureLoaded()) { - return chromeos::GetCurrentKeyboardLayoutName(); +class KeyboardLibraryImpl : public KeyboardLibrary { + public: + KeyboardLibraryImpl() {} + virtual ~KeyboardLibraryImpl() {} + + std::string GetHardwareKeyboardLayoutName() const { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::GetHardwareKeyboardLayoutName(); + } + return ""; } - return ""; -} -bool KeyboardLibraryImpl::SetCurrentKeyboardLayoutByName( - const std::string& layout_name) { - if (CrosLibrary::Get()->EnsureLoaded()) { - return chromeos::SetCurrentKeyboardLayoutByName(layout_name); + std::string GetCurrentKeyboardLayoutName() const { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::GetCurrentKeyboardLayoutName(); + } + return ""; } - return false; -} -bool KeyboardLibraryImpl::GetKeyboardLayoutPerWindow( - bool* is_per_window) const { - if (CrosLibrary::Get()->EnsureLoaded()) { - return chromeos::GetKeyboardLayoutPerWindow(is_per_window); + bool SetCurrentKeyboardLayoutByName(const std::string& layout_name) { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::SetCurrentKeyboardLayoutByName(layout_name); + } + return false; } - return false; -} -bool KeyboardLibraryImpl::SetKeyboardLayoutPerWindow(bool is_per_window) { - if (CrosLibrary::Get()->EnsureLoaded()) { - return chromeos::SetKeyboardLayoutPerWindow(is_per_window); + bool RemapModifierKeys(const ModifierMap& modifier_map) { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::RemapModifierKeys(modifier_map); + } + return false; + } + + bool GetKeyboardLayoutPerWindow(bool* is_per_window) const { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::GetKeyboardLayoutPerWindow(is_per_window); + } + return false; + } + + bool SetKeyboardLayoutPerWindow(bool is_per_window) { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::SetKeyboardLayoutPerWindow(is_per_window); + } + return false; + } + + bool GetAutoRepeatEnabled(bool* enabled) const { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::GetAutoRepeatEnabled(enabled); + } + return false; + } + + bool SetAutoRepeatEnabled(bool enabled) { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::SetAutoRepeatEnabled(enabled); + } + return false; + } + + bool GetAutoRepeatRate(AutoRepeatRate* out_rate) const { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::GetAutoRepeatRate(out_rate); + } + return false; + } + + bool SetAutoRepeatRate(const AutoRepeatRate& rate) { + if (CrosLibrary::Get()->EnsureLoaded()) { + return chromeos::SetAutoRepeatRate(rate); + } + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardLibraryImpl); +}; + +class KeyboardLibraryStubImpl : public KeyboardLibrary { + public: + KeyboardLibraryStubImpl() {} + virtual ~KeyboardLibraryStubImpl() {} + + std::string GetHardwareKeyboardLayoutName() const { + return "xkb:us::eng"; } - return false; + + std::string GetCurrentKeyboardLayoutName() const { + return ""; + } + + bool SetCurrentKeyboardLayoutByName(const std::string& layout_name) { + return false; + } + + bool RemapModifierKeys(const ModifierMap& modifier_map) { + return false; + } + + bool GetKeyboardLayoutPerWindow(bool* is_per_window) const { + return false; + } + + bool SetKeyboardLayoutPerWindow(bool is_per_window) { + return false; + } + + bool GetAutoRepeatEnabled(bool* enabled) const { + return false; + } + + bool SetAutoRepeatEnabled(bool enabled) { + return false; + } + + bool GetAutoRepeatRate(AutoRepeatRate* out_rate) const { + return false; + } + + bool SetAutoRepeatRate(const AutoRepeatRate& rate) { + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardLibraryStubImpl); +}; + +// static +KeyboardLibrary* KeyboardLibrary::GetImpl(bool stub) { + if (stub) + return new KeyboardLibraryStubImpl(); + else + return new KeyboardLibraryImpl(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/keyboard_library.h b/chrome/browser/chromeos/cros/keyboard_library.h index 17e1b42..b5f0deb 100644 --- a/chrome/browser/chromeos/cros/keyboard_library.h +++ b/chrome/browser/chromeos/cros/keyboard_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_KEYBOARD_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_KEYBOARD_LIBRARY_H_ +#pragma once #include "cros/chromeos_keyboard.h" @@ -18,6 +19,9 @@ class KeyboardLibrary { public: virtual ~KeyboardLibrary() {} + // Returns the hardware layout name like "xkb:us::eng". On error, returns "". + virtual std::string GetHardwareKeyboardLayoutName() const = 0; + // Returns the current layout name like "us". On error, returns "". virtual std::string GetCurrentKeyboardLayoutName() const = 0; @@ -26,6 +30,9 @@ class KeyboardLibrary { virtual bool SetCurrentKeyboardLayoutByName( const std::string& layout_name) = 0; + // Remaps modifier keys. Returns true on success. + virtual bool RemapModifierKeys(const ModifierMap& modifier_map) = 0; + // Gets whehter we have separate keyboard layout per window, or not. The // result is stored in |is_per_window|. Returns true on success. virtual bool GetKeyboardLayoutPerWindow(bool* is_per_window) const = 0; @@ -34,21 +41,25 @@ class KeyboardLibrary { // is given, the same keyboard layout will be shared for all applications. // Returns true on success. virtual bool SetKeyboardLayoutPerWindow(bool is_per_window) = 0; -}; -class KeyboardLibraryImpl : public KeyboardLibrary { - public: - KeyboardLibraryImpl() {} - virtual ~KeyboardLibraryImpl() {} + // Gets the current auto-repeat mode of the keyboard. The result is stored in + // |enabled|. Returns true on success. + virtual bool GetAutoRepeatEnabled(bool* enabled) const = 0; + + // Turns on and off the auto-repeat of the keyboard. Returns true on success. + virtual bool SetAutoRepeatEnabled(bool enabled) = 0; + + // Gets the current auto-repeat rate of the keyboard. The result is stored in + // |out_rate|. Returns true on success. + virtual bool GetAutoRepeatRate(AutoRepeatRate* out_rate) const = 0; - // KeyboardLibrary overrides. - virtual std::string GetCurrentKeyboardLayoutName() const; - virtual bool SetCurrentKeyboardLayoutByName(const std::string& layout_name); - virtual bool GetKeyboardLayoutPerWindow(bool* is_per_window) const; - virtual bool SetKeyboardLayoutPerWindow(bool is_per_window); + // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat + // interval in ms. Returns true on success. + virtual bool SetAutoRepeatRate(const AutoRepeatRate& rate) = 0; - private: - DISALLOW_COPY_AND_ASSIGN(KeyboardLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static KeyboardLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/login_library.cc b/chrome/browser/chromeos/cros/login_library.cc index efd8536..4b57d44 100644 --- a/chrome/browser/chromeos/cros/login_library.cc +++ b/chrome/browser/chromeos/cros/login_library.cc @@ -10,19 +10,227 @@ namespace chromeos { -bool LoginLibraryImpl::EmitLoginPromptReady() { - return chromeos::EmitLoginPromptReady(); -} +class LoginLibraryImpl : public LoginLibrary { + public: + LoginLibraryImpl() + : set_owner_key_callback_(NULL), + whitelist_op_callback_(NULL), + property_op_callback_(NULL) { + if (CrosLibrary::Get()->EnsureLoaded()) + Init(); + } + virtual ~LoginLibraryImpl() { + if (session_connection_) { + chromeos::DisconnectSession(session_connection_); + } + } -bool LoginLibraryImpl::StartSession(const std::string& user_email, - const std::string& unique_id /* unused */) { - // only pass unique_id through once we use it for something. - return chromeos::StartSession(user_email.c_str(), ""); -} + bool EmitLoginPromptReady() { + return chromeos::EmitLoginPromptReady(); + } + + bool CheckWhitelist(const std::string& email, + std::vector<uint8>* OUT_signature) { + return chromeos::CheckWhitelist(email.c_str(), OUT_signature); + } + + bool RetrieveProperty(const std::string& name, + std::string* OUT_value, + std::vector<uint8>* OUT_signature) { + return chromeos::RetrieveProperty(name.c_str(), OUT_value, OUT_signature); + } + + bool SetOwnerKeyAsync(const std::vector<uint8>& public_key_der, + Delegate* callback) { + DCHECK(callback) << "must provide a callback to SetOwnerKeyAsync()"; + if (set_owner_key_callback_) + return false; + set_owner_key_callback_ = callback; + return chromeos::SetOwnerKey(public_key_der); + } + + bool StorePropertyAsync(const std::string& name, + const std::string& value, + const std::vector<uint8>& signature, + Delegate* callback) { + DCHECK(callback) << "must provide a callback to StorePropertyAsync()"; + if (property_op_callback_) + return false; + property_op_callback_ = callback; + return chromeos::StoreProperty(name.c_str(), value.c_str(), signature); + } + + bool UnwhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) { + DCHECK(callback) << "must provide a callback to UnwhitelistAsync()"; + if (whitelist_op_callback_) + return false; + whitelist_op_callback_ = callback; + return chromeos::Unwhitelist(email.c_str(), signature); + } + + bool WhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) { + DCHECK(callback) << "must provide a callback to WhitelistAsync()"; + if (whitelist_op_callback_) + return false; + whitelist_op_callback_ = callback; + return chromeos::Whitelist(email.c_str(), signature); + } + + bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) { + return chromeos::EnumerateWhitelisted(whitelisted); + } + + bool StartSession(const std::string& user_email, + const std::string& unique_id /* unused */) { + // only pass unique_id through once we use it for something. + return chromeos::StartSession(user_email.c_str(), ""); + } + + bool StopSession(const std::string& unique_id /* unused */) { + // only pass unique_id through once we use it for something. + return chromeos::StopSession(""); + } + + bool RestartJob(int pid, const std::string& command_line) { + return chromeos::RestartJob(pid, command_line.c_str()); + } + + private: + static void Handler(void* object, const OwnershipEvent& event) { + LoginLibraryImpl* self = static_cast<LoginLibraryImpl*>(object); + switch (event) { + case SetKeySuccess: + self->CompleteSetOwnerKey(true); + break; + case SetKeyFailure: + self->CompleteSetOwnerKey(false); + break; + case WhitelistOpSuccess: + self->CompleteWhitelistOp(true); + break; + case WhitelistOpFailure: + self->CompleteWhitelistOp(false); + break; + case PropertyOpSuccess: + self->CompletePropertyOp(true); + break; + case PropertyOpFailure: + self->CompletePropertyOp(false); + break; + default: + NOTREACHED(); + break; + } + } + + void Init() { + session_connection_ = chromeos::MonitorSession(&Handler, this); + } + + void CompleteSetOwnerKey(bool result) { + CHECK(set_owner_key_callback_) << "CompleteSetOwnerKey() called without " + "a registered callback!"; + set_owner_key_callback_->OnComplete(result); + set_owner_key_callback_ = NULL; + } + + void CompleteWhitelistOp(bool result) { + CHECK(whitelist_op_callback_); + whitelist_op_callback_->OnComplete(result); + whitelist_op_callback_ = NULL; + } + + void CompletePropertyOp(bool result) { + CHECK(property_op_callback_); + property_op_callback_->OnComplete(result); + property_op_callback_ = NULL; + } + + chromeos::SessionConnection session_connection_; + + Delegate* set_owner_key_callback_; + Delegate* whitelist_op_callback_; + Delegate* property_op_callback_; + + DISALLOW_COPY_AND_ASSIGN(LoginLibraryImpl); +}; + +class LoginLibraryStubImpl : public LoginLibrary { + public: + LoginLibraryStubImpl() {} + virtual ~LoginLibraryStubImpl() {} + + bool EmitLoginPromptReady() { return true; } + bool CheckWhitelist(const std::string& email, + std::vector<uint8>* OUT_signature) { + OUT_signature->assign(2, 0); + return true; + } + bool RetrieveProperty(const std::string& name, + std::string* OUT_value, + std::vector<uint8>* OUT_signature) { + OUT_value->assign("stub"); + OUT_signature->assign(2, 0); + return true; + } + bool SetOwnerKeyAsync(const std::vector<uint8>& public_key_der, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + bool StorePropertyAsync(const std::string& name, + const std::string& value, + const std::vector<uint8>& signature, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + bool UnwhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + bool WhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(&DoStubCallback, callback)); + return true; + } + bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) { + return true; + } + bool StartSession(const std::string& user_email, + const std::string& unique_id /* unused */) { return true; } + bool StopSession(const std::string& unique_id /* unused */) { return true; } + bool RestartJob(int pid, const std::string& command_line) { return true; } + + private: + static void DoStubCallback(Delegate* callback) { + callback->OnComplete(true); + } + + DISALLOW_COPY_AND_ASSIGN(LoginLibraryStubImpl); +}; -bool LoginLibraryImpl::StopSession(const std::string& unique_id /* unused */) { - // only pass unique_id through once we use it for something. - return chromeos::StopSession(""); +// static +LoginLibrary* LoginLibrary::GetImpl(bool stub) { + if (stub) + return new LoginLibraryStubImpl(); + else + return new LoginLibraryImpl(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/login_library.h b/chrome/browser/chromeos/cros/login_library.h index 3e8936f..fc9b11a 100644 --- a/chrome/browser/chromeos/cros/login_library.h +++ b/chrome/browser/chromeos/cros/login_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_LOGIN_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_LOGIN_LIBRARY_H_ +#pragma once #include <string> @@ -15,10 +16,72 @@ namespace chromeos { // This interface defines the interaction with the ChromeOS login library APIs. class LoginLibrary { public: + class Delegate { + public: + virtual void OnComplete(bool value) = 0; + }; + virtual ~LoginLibrary() {} // Requests that the Upstart signal login-prompt-ready be emitted. virtual bool EmitLoginPromptReady() = 0; + // Check whether or not |email| is present on the whitelist. + // If so, we return true and store the signature passed when |email| was + // whitelisted in |OUT_signature|. + // If not, we return false and don't touch the output parameter. + virtual bool CheckWhitelist(const std::string& email, + std::vector<uint8>* OUT_signature) = 0; + + // Fetch the value associated with |name|, if its present. + // If so, we return true, store the info in |OUT_value|, and store the + // signature passed when the property was initially stored in |OUT_signature|. + // If not, we return false and don't touch the output parameters. + virtual bool RetrieveProperty(const std::string& name, + std::string* OUT_value, + std::vector<uint8>* OUT_signature) = 0; + + // Attempts to asynchronously set the provided public key as the + // Owner's public key for this device. |public_key_der| should be a + // DER-encoded PKCS11 SubjectPublicKeyInfo structure. + // Returns true if the attempt was successfully started. + // callback->Run() will be called when the operation is complete. + virtual bool SetOwnerKeyAsync(const std::vector<uint8>& public_key_der, + Delegate* callback) = 0; + + // Attempts to issue a signed async request to store |name|=|value|. + // |signature| must by a SHA1 with RSA encryption signature over the string + // "name=value" with the owner's private key. + // Returns true if the attempt was successfully started. + // callback->Run() will be called when the operation is complete. + virtual bool StorePropertyAsync(const std::string& name, + const std::string& value, + const std::vector<uint8>& signature, + Delegate* callback) = 0; + + // Attempts to issue a signed async request to whitelist |email|. + // |signature| must by a SHA1 with RSA encryption signature over |email| + // with the owner's private key. + // Returns true if the attempt was successfully started. + // callback->Run() will be called when the operation is complete. + virtual bool WhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) = 0; + + // Attempts to issue a signed async request to remove |email| from the + // whitelist of users allowed to log in to this machine. + // |signature| must by a SHA1 with RSA encryption signature over |email| + // with the owner's private key. + // Returns true if the attempt was successfully started. + // callback->Run() will be called when the operation is complete. + virtual bool UnwhitelistAsync(const std::string& email, + const std::vector<uint8>& signature, + Delegate* callback) = 0; + + // Retrieves the user white list. Note the call is for display purpose only. + // To determine if an email is white listed, you MUST use CheckWhitelist. + // Returns true if the request is successfully dispatched. + virtual bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) = 0; + // Tells the session manager to start a logged-in session for the user // |user_email|. |unique_id| is meant to be used when we have a non-human- // readable unique identifier by which we distinguish users (to deal with @@ -31,22 +94,13 @@ class LoginLibrary { // This will tell the session manager to terminate the session for the user // indicated by |unique_id|. virtual bool StopSession(const std::string& unique_id /* unused */) = 0; -}; -// This class handles the interaction with the ChromeOS login library APIs. -class LoginLibraryImpl : public LoginLibrary { - public: - LoginLibraryImpl() {} - virtual ~LoginLibraryImpl() {} - - // LoginLibrary overrides. - virtual bool EmitLoginPromptReady(); - virtual bool StartSession(const std::string& user_email, - const std::string& unique_id /* unused */); - virtual bool StopSession(const std::string& unique_id /* unused */); + // Restarts the job with specified command line string. + virtual bool RestartJob(int pid, const std::string& command_line) = 0; - private: - DISALLOW_COPY_AND_ASSIGN(LoginLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static LoginLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/mock_cros_library.h b/chrome/browser/chromeos/cros/mock_cros_library.h index f66cc67..19434fe 100644 --- a/chrome/browser/chromeos/cros/mock_cros_library.h +++ b/chrome/browser/chromeos/cros/mock_cros_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_CROS_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_CROS_LIBRARY_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/cros/mock_cryptohome_library.h b/chrome/browser/chromeos/cros/mock_cryptohome_library.h index 664f88a..c579418 100644 --- a/chrome/browser/chromeos/cros/mock_cryptohome_library.h +++ b/chrome/browser/chromeos/cros/mock_cryptohome_library.h @@ -4,30 +4,79 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_CRYPTOHOME_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_CRYPTOHOME_LIBRARY_H_ +#pragma once #include <string> #include "chrome/browser/chromeos/cros/cryptohome_library.h" #include "testing/gmock/include/gmock/gmock.h" +using ::testing::Invoke; +using ::testing::WithArgs; +using ::testing::_; + namespace chromeos { class MockCryptohomeLibrary : public CryptohomeLibrary { public: - MockCryptohomeLibrary() {} + MockCryptohomeLibrary() { + ON_CALL(*this, AsyncCheckKey(_, _, _)) + .WillByDefault( + WithArgs<2>(Invoke(this, &MockCryptohomeLibrary::DoCallback))); + ON_CALL(*this, AsyncMigrateKey(_, _, _, _)) + .WillByDefault( + WithArgs<3>(Invoke(this, &MockCryptohomeLibrary::DoCallback))); + ON_CALL(*this, AsyncMount(_, _, _, _)) + .WillByDefault( + WithArgs<3>(Invoke(this, &MockCryptohomeLibrary::DoCallback))); + ON_CALL(*this, AsyncMountForBwsi(_)) + .WillByDefault( + WithArgs<0>(Invoke(this, &MockCryptohomeLibrary::DoCallback))); + ON_CALL(*this, AsyncRemove(_, _)) + .WillByDefault( + WithArgs<1>(Invoke(this, &MockCryptohomeLibrary::DoCallback))); + } virtual ~MockCryptohomeLibrary() {} - MOCK_METHOD3(Mount, bool(const std::string& user_email, - const std::string& passhash, - int* error_code)); - MOCK_METHOD1(MountForBwsi, bool(int*)); MOCK_METHOD2(CheckKey, bool(const std::string& user_email, const std::string& passhash)); + MOCK_METHOD3(AsyncCheckKey, bool(const std::string& user_email, + const std::string& passhash, + Delegate* callback)); MOCK_METHOD3(MigrateKey, bool(const std::string& user_email, const std::string& old_hash, const std::string& new_hash)); + MOCK_METHOD4(AsyncMigrateKey, bool(const std::string& user_email, + const std::string& old_hash, + const std::string& new_hash, + Delegate* callback)); + MOCK_METHOD3(Mount, bool(const std::string& user_email, + const std::string& passhash, + int* error_code)); + MOCK_METHOD4(AsyncMount, bool(const std::string& user_email, + const std::string& passhash, + const bool create_if_missing, + Delegate* callback)); + MOCK_METHOD1(MountForBwsi, bool(int*)); + MOCK_METHOD1(AsyncMountForBwsi, bool(Delegate* callback)); MOCK_METHOD1(Remove, bool(const std::string& user_email)); + MOCK_METHOD2(AsyncRemove, bool(const std::string& user_email, Delegate* d)); MOCK_METHOD0(IsMounted, bool(void)); MOCK_METHOD0(GetSystemSalt, CryptohomeBlob(void)); + + void SetAsyncBehavior(bool outcome, int code) { + outcome_ = outcome; + code_ = code; + } + + bool DoCallback(Delegate* d) { + d->OnComplete(outcome_, code_); + return true; + } + + private: + bool outcome_; + int code_; + DISALLOW_COPY_AND_ASSIGN(MockCryptohomeLibrary); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/mock_input_method_library.h b/chrome/browser/chromeos/cros/mock_input_method_library.h index ccd0387..4cbd1c8 100644 --- a/chrome/browser/chromeos/cros/mock_input_method_library.h +++ b/chrome/browser/chromeos/cros/mock_input_method_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_INPUT_METHOD_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_INPUT_METHOD_LIBRARY_H_ +#pragma once #include <string> @@ -32,9 +33,11 @@ class MockInputMethodLibrary : public InputMethodLibrary { MOCK_CONST_METHOD0(previous_input_method, const InputMethodDescriptor&(void)); MOCK_CONST_METHOD0(current_input_method, const InputMethodDescriptor&(void)); MOCK_CONST_METHOD0(current_ime_properties, const ImePropertyList&(void)); + MOCK_METHOD0(StartInputMethodProcesses, void(void)); + MOCK_METHOD0(StopInputMethodProcesses, void(void)); + MOCK_METHOD1(SetDeferImeStartup, void(bool)); }; } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_MOCK_INPUT_METHOD_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros/mock_keyboard_library.h b/chrome/browser/chromeos/cros/mock_keyboard_library.h index 67bcdce..f36d74f 100644 --- a/chrome/browser/chromeos/cros/mock_keyboard_library.h +++ b/chrome/browser/chromeos/cros/mock_keyboard_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_KEYBOARD_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_KEYBOARD_LIBRARY_H_ +#pragma once #include <string> @@ -17,10 +18,16 @@ class MockKeyboardLibrary : public KeyboardLibrary { MockKeyboardLibrary() {} virtual ~MockKeyboardLibrary() {} + MOCK_CONST_METHOD0(GetHardwareKeyboardLayoutName, std::string(void)); MOCK_CONST_METHOD0(GetCurrentKeyboardLayoutName, std::string(void)); MOCK_METHOD1(SetCurrentKeyboardLayoutByName, bool(const std::string&)); + MOCK_METHOD1(RemapModifierKeys, bool(const ModifierMap&)); MOCK_CONST_METHOD1(GetKeyboardLayoutPerWindow, bool(bool*)); MOCK_METHOD1(SetKeyboardLayoutPerWindow, bool(bool)); + MOCK_CONST_METHOD1(GetAutoRepeatEnabled, bool(bool*)); + MOCK_METHOD1(SetAutoRepeatEnabled, bool(bool)); + MOCK_CONST_METHOD1(GetAutoRepeatRate, bool(AutoRepeatRate*)); + MOCK_METHOD1(SetAutoRepeatRate, bool(const AutoRepeatRate&)); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/mock_library_loader.h b/chrome/browser/chromeos/cros/mock_library_loader.h index 5863a54..1e4a440 100644 --- a/chrome/browser/chromeos/cros/mock_library_loader.h +++ b/chrome/browser/chromeos/cros/mock_library_loader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_LIBRARY_LOADER_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_LIBRARY_LOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/cros/mock_login_library.h b/chrome/browser/chromeos/cros/mock_login_library.h index 51096bf..43a77b4 100644 --- a/chrome/browser/chromeos/cros/mock_login_library.h +++ b/chrome/browser/chromeos/cros/mock_login_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_LOGIN_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_LOGIN_LIBRARY_H_ +#pragma once #include <string> @@ -16,13 +17,28 @@ class MockLoginLibrary : public LoginLibrary { public: MockLoginLibrary() {} virtual ~MockLoginLibrary() {} + MOCK_METHOD2(CheckWhitelist, bool(const std::string&, std::vector<uint8>*)); MOCK_METHOD0(EmitLoginPromptReady, bool(void)); + MOCK_METHOD1(EnumerateWhitelisted, bool(std::vector<std::string>*)); + MOCK_METHOD3(RetrieveProperty, bool(const std::string&, + std::string*, + std::vector<uint8>*)); + MOCK_METHOD2(SetOwnerKeyAsync, bool(const std::vector<uint8>&, Delegate*)); + MOCK_METHOD4(StorePropertyAsync, bool(const std::string&, + const std::string&, + const std::vector<uint8>&, + Delegate*)); + MOCK_METHOD3(UnwhitelistAsync, bool(const std::string&, + const std::vector<uint8>&, + Delegate*)); + MOCK_METHOD3(WhitelistAsync, bool(const std::string&, + const std::vector<uint8>&, + Delegate*)); MOCK_METHOD2(StartSession, bool(const std::string&, const std::string&)); - MOCK_METHOD1(StartSession, bool(const std::string&)); MOCK_METHOD1(StopSession, bool(const std::string&)); + MOCK_METHOD2(RestartJob, bool(int, const std::string&)); }; } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_MOCK_LOGIN_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros/mock_mount_library.h b/chrome/browser/chromeos/cros/mock_mount_library.h index e0309ea..32a52c2 100644 --- a/chrome/browser/chromeos/cros/mock_mount_library.h +++ b/chrome/browser/chromeos/cros/mock_mount_library.h @@ -1,25 +1,22 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_CROS_MOCK_MOUNT_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_MOUNT_LIBRARY_H_ +#pragma once #include <string> -#include <vector> #include "base/observer_list.h" #include "base/time.h" -#include "cros/chromeos_mount.h" #include "chrome/browser/chromeos/cros/mount_library.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "cros/chromeos_mount.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chromeos { -// This class handles the interaction with the ChromeOS mount library APIs. -// Classes can add themselves as observers. Users can get an instance of this -// library class like this: MountLibrary::Get(). class MockMountLibrary : public MountLibrary { public: MockMountLibrary(); diff --git a/chrome/browser/chromeos/cros/mock_network_library.h b/chrome/browser/chromeos/cros/mock_network_library.h index ee87358..823cd5b 100644 --- a/chrome/browser/chromeos/cros/mock_network_library.h +++ b/chrome/browser/chromeos/cros/mock_network_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_NETWORK_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_NETWORK_LIBRARY_H_ +#pragma once #include <string> @@ -49,9 +50,6 @@ class MockNetworkLibrary : public NetworkLibrary { MOCK_METHOD0(RequestWifiScan, void(void)); MOCK_METHOD0(UpdateSystemInfo, void(void)); MOCK_METHOD1(GetWifiAccessPoints, bool(WifiAccessPointVector*)); - MOCK_METHOD0(ConnectToPreferredNetworkIfAvailable, bool(void)); - MOCK_METHOD0(PreferredNetworkConnected, bool(void)); - MOCK_METHOD0(PreferredNetworkFailed, bool(void)); MOCK_METHOD4(ConnectToWifiNetwork, void(WifiNetwork, const std::string&, const std::string&, @@ -65,7 +63,7 @@ class MockNetworkLibrary : public NetworkLibrary { MOCK_METHOD1(DisconnectFromWirelessNetwork, void(const WirelessNetwork&)); MOCK_METHOD1(SaveCellularNetwork, void(const CellularNetwork&)); MOCK_METHOD1(SaveWifiNetwork, void(const WifiNetwork&)); - MOCK_METHOD1(ForgetWirelessNetwork, void(const WirelessNetwork&)); + MOCK_METHOD1(ForgetWirelessNetwork, void(const std::string&)); MOCK_CONST_METHOD0(ethernet_available, bool(void)); MOCK_CONST_METHOD0(wifi_available, bool(void)); diff --git a/chrome/browser/chromeos/cros/mock_power_library.h b/chrome/browser/chromeos/cros/mock_power_library.h index 4b57538..fae5521 100644 --- a/chrome/browser/chromeos/cros/mock_power_library.h +++ b/chrome/browser/chromeos/cros/mock_power_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_POWER_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_POWER_LIBRARY_H_ +#pragma once #include "chrome/browser/chromeos/cros/power_library.h" #include "testing/gmock/include/gmock/gmock.h" diff --git a/chrome/browser/chromeos/cros/mock_screen_lock_library.h b/chrome/browser/chromeos/cros/mock_screen_lock_library.h index 9022ce6..9b1c01c 100644 --- a/chrome/browser/chromeos/cros/mock_screen_lock_library.h +++ b/chrome/browser/chromeos/cros/mock_screen_lock_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_SCREEN_LOCK_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_SCREEN_LOCK_LIBRARY_H_ +#pragma once #include "chrome/browser/chromeos/cros/screen_lock_library.h" #include "testing/gmock/include/gmock/gmock.h" diff --git a/chrome/browser/chromeos/cros/mock_speech_synthesis_library.h b/chrome/browser/chromeos/cros/mock_speech_synthesis_library.h index 08cf883..dcf1926 100644 --- a/chrome/browser/chromeos/cros/mock_speech_synthesis_library.h +++ b/chrome/browser/chromeos/cros/mock_speech_synthesis_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_SPEECH_SYNTHESIS_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_SPEECH_SYNTHESIS_LIBRARY_H_ +#pragma once #include "chrome/browser/chromeos/cros/speech_synthesis_library.h" #include "testing/gmock/include/gmock/gmock.h" diff --git a/chrome/browser/chromeos/cros/mock_synaptics_library.h b/chrome/browser/chromeos/cros/mock_synaptics_library.h deleted file mode 100644 index be5efb0..0000000 --- a/chrome/browser/chromeos/cros/mock_synaptics_library.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2010 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_CROS_MOCK_SYNAPTICS_LIBRARY_H_ -#define CHROME_BROWSER_CHROMEOS_CROS_MOCK_SYNAPTICS_LIBRARY_H_ - -#include "chrome/browser/chromeos/cros/synaptics_library.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace chromeos { - -class MockSynapticsLibrary : public SynapticsLibrary { - public: - MockSynapticsLibrary() {} - virtual ~MockSynapticsLibrary() {} - MOCK_METHOD2(SetBoolParameter, void(SynapticsParameter, bool)); - MOCK_METHOD2(SetRangeParameter, void(SynapticsParameter, int)); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_CROS_MOCK_SYNAPTICS_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros/mock_system_library.h b/chrome/browser/chromeos/cros/mock_system_library.h index bbb8006..a851238 100644 --- a/chrome/browser/chromeos/cros/mock_system_library.h +++ b/chrome/browser/chromeos/cros/mock_system_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_SYSTEM_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_SYSTEM_LIBRARY_H_ +#pragma once #include "chrome/browser/chromeos/cros/system_library.h" #include "testing/gmock/include/gmock/gmock.h" @@ -18,9 +19,9 @@ class MockSystemLibrary : public SystemLibrary { MOCK_METHOD1(RemoveObserver, void(Observer*)); MOCK_METHOD0(GetTimezone, const icu::TimeZone&()); MOCK_METHOD1(SetTimezone, void(const icu::TimeZone*)); + MOCK_METHOD2(GetMachineStatistic, bool(const std::string&, std::string*)); }; } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_MOCK_SYSTEM_LIBRARY_H_ - diff --git a/chrome/browser/chromeos/cros/mock_update_library.h b/chrome/browser/chromeos/cros/mock_update_library.h index 4e1e7f6..d4f14cb 100644 --- a/chrome/browser/chromeos/cros/mock_update_library.h +++ b/chrome/browser/chromeos/cros/mock_update_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOCK_UPDATE_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOCK_UPDATE_LIBRARY_H_ +#pragma once #include "base/observer_list.h" #include "chrome/browser/chromeos/cros/update_library.h" @@ -17,7 +18,8 @@ class MockUpdateLibrary : public UpdateLibrary { virtual ~MockUpdateLibrary() {} MOCK_METHOD1(AddObserver, void(Observer*)); // NOLINT MOCK_METHOD1(RemoveObserver, void(Observer*)); // NOLINT - + MOCK_METHOD0(CheckForUpdate, bool(void)); + MOCK_METHOD0(RebootAfterUpdate, bool(void)); MOCK_CONST_METHOD0(status, const Status&(void)); private: diff --git a/chrome/browser/chromeos/cros/mount_library.cc b/chrome/browser/chromeos/cros/mount_library.cc index e99edf2..16cec4a 100644 --- a/chrome/browser/chromeos/cros/mount_library.cc +++ b/chrome/browser/chromeos/cros/mount_library.cc @@ -9,97 +9,140 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::MountLibraryImpl); - namespace chromeos { -void MountLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} +class MountLibraryImpl : public MountLibrary { + public: + MountLibraryImpl() : mount_status_connection_(NULL) { + if (CrosLibrary::Get()->EnsureLoaded()) { + Init(); + } else { + LOG(ERROR) << "Cros Library has not been loaded"; + } + } -void MountLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + ~MountLibraryImpl() { + if (mount_status_connection_) { + DisconnectMountStatus(mount_status_connection_); + } + } -bool MountLibraryImpl::MountPath(const char* device_path) { - return MountDevicePath(device_path); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void MountLibraryImpl::ParseDisks(const MountStatus& status) { - disks_.clear(); - for (int i = 0; i < status.size; i++) { - std::string path; - std::string mountpath; - std::string systempath; - bool parent; - bool hasmedia; - if (status.disks[i].path != NULL) { - path = status.disks[i].path; - } - if (status.disks[i].mountpath != NULL) { - mountpath = status.disks[i].mountpath; - } - if (status.disks[i].systempath != NULL) { - systempath = status.disks[i].systempath; + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } + + bool MountPath(const char* device_path) { + return MountDevicePath(device_path); + } + + const DiskVector& disks() const { return disks_; } + + private: + void ParseDisks(const MountStatus& status) { + disks_.clear(); + for (int i = 0; i < status.size; i++) { + std::string path; + std::string mountpath; + std::string systempath; + bool parent; + bool hasmedia; + if (status.disks[i].path != NULL) { + path = status.disks[i].path; + } + if (status.disks[i].mountpath != NULL) { + mountpath = status.disks[i].mountpath; + } + if (status.disks[i].systempath != NULL) { + systempath = status.disks[i].systempath; + } + parent = status.disks[i].isparent; + hasmedia = status.disks[i].hasmedia; + disks_.push_back(Disk(path, + mountpath, + systempath, + parent, + hasmedia)); } - parent = status.disks[i].isparent; - hasmedia = status.disks[i].hasmedia; - disks_.push_back(Disk(path, - mountpath, - systempath, - parent, - hasmedia)); } -} -MountLibraryImpl::MountLibraryImpl() : mount_status_connection_(NULL) { - if (CrosLibrary::Get()->EnsureLoaded()) { - Init(); - } else { - LOG(ERROR) << "Cros Library has not been loaded"; + static void MountStatusChangedHandler(void* object, + const MountStatus& status, + MountEventType evt, + const char* path) { + MountLibraryImpl* mount = static_cast<MountLibraryImpl*>(object); + std::string devicepath = path; + mount->ParseDisks(status); + mount->UpdateMountStatus(status, evt, devicepath); } -} -MountLibraryImpl::~MountLibraryImpl() { - if (mount_status_connection_) { - DisconnectMountStatus(mount_status_connection_); + void Init() { + // Getting the monitor status so that the daemon starts up. + MountStatus* mount = RetrieveMountInformation(); + if (!mount) { + LOG(ERROR) << "Failed to retrieve mount information"; + return; + } + ParseDisks(*mount); + FreeMountStatus(mount); + + mount_status_connection_ = MonitorMountStatus( + &MountStatusChangedHandler, this); } -} -// static -void MountLibraryImpl::MountStatusChangedHandler(void* object, - const MountStatus& status, - MountEventType evt, - const char* path) { - MountLibraryImpl* mount = static_cast<MountLibraryImpl*>(object); - std::string devicepath = path; - mount->ParseDisks(status); - mount->UpdateMountStatus(status, evt, devicepath); -} + void UpdateMountStatus(const MountStatus& status, + MountEventType evt, + const std::string& path) { + // Make sure we run on UI thread. + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); -void MountLibraryImpl::Init() { - // Getting the monitor status so that the daemon starts up. - MountStatus* mount = RetrieveMountInformation(); - if (!mount) { - LOG(ERROR) << "Failed to retrieve mount information"; - return; + FOR_EACH_OBSERVER( + Observer, observers_, MountChanged(this, evt, path)); } - ParseDisks(*mount); - FreeMountStatus(mount); + ObserverList<Observer> observers_; - mount_status_connection_ = MonitorMountStatus( - &MountStatusChangedHandler, this); -} + // A reference to the mount api, to allow callbacks when the mount + // status changes. + MountStatusConnection mount_status_connection_; + + // The list of disks found. + DiskVector disks_; + + DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl); +}; -void MountLibraryImpl::UpdateMountStatus(const MountStatus& status, - MountEventType evt, - const std::string& path) { - // Make sure we run on UI thread. - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); +class MountLibraryStubImpl : public MountLibrary { + public: + MountLibraryStubImpl() {} + virtual ~MountLibraryStubImpl() {} - FOR_EACH_OBSERVER(Observer, observers_, MountChanged(this, evt, path)); + // MountLibrary overrides. + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual const DiskVector& disks() const { return disks_; } + virtual bool MountPath(const char* device_path) { return false; } + + private: + // The list of disks found. + DiskVector disks_; + + DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl); +}; + +// static +MountLibrary* MountLibrary::GetImpl(bool stub) { + if (stub) + return new MountLibraryStubImpl(); + else + return new MountLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::MountLibraryImpl); + diff --git a/chrome/browser/chromeos/cros/mount_library.h b/chrome/browser/chromeos/cros/mount_library.h index 3fd772a..f54ba53 100644 --- a/chrome/browser/chromeos/cros/mount_library.h +++ b/chrome/browser/chromeos/cros/mount_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_MOUNT_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_MOUNT_LIBRARY_H_ +#pragma once #include <string> #include <vector> @@ -17,7 +18,7 @@ namespace chromeos { // This class handles the interaction with the ChromeOS mount library APIs. // Classes can add themselves as observers. Users can get an instance of this -// library class like this: MountLibrary::Get(). +// library class like this: chromeos::CrosLibrary::Get()->GetMountLibrary() class MountLibrary { public: // Used to house an instance of each found mount device. @@ -58,51 +59,10 @@ class MountLibrary { virtual void RemoveObserver(Observer* observer) = 0; virtual const DiskVector& disks() const = 0; virtual bool MountPath(const char* device_path) = 0; -}; - -// This class handles the interaction with the ChromeOS mount library APIs. -// Classes can add themselves as observers. Users can get an instance of this -// library class like this: MountLibrary::Get(). -class MountLibraryImpl : public MountLibrary { - public: - MountLibraryImpl(); - virtual ~MountLibraryImpl(); - - // MountLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - virtual const DiskVector& disks() const { return disks_; } - virtual bool MountPath(const char* device_path); - private: - void ParseDisks(const MountStatus& status); - - // This method is called when there's a change in mount status. - // This method is called the UI Thread. - static void MountStatusChangedHandler(void* object, - const MountStatus& status, - MountEventType evt, - const char* path); - - // This methods starts the monitoring of mount changes. - // It should be called on the UI Thread. - void Init(); - - // Called by the handler to update the mount status. - // This will notify all the Observers. - void UpdateMountStatus(const MountStatus& status, - MountEventType evt, - const std::string& path); - - ObserverList<Observer> observers_; - - // A reference to the mount api, to allow callbacks when the mount - // status changes. - MountStatusConnection mount_status_connection_; - - // The list of disks found. - DiskVector disks_; - DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static MountLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/network_library.cc b/chrome/browser/chromeos/cros/network_library.cc index 7538111..2258a36 100644 --- a/chrome/browser/chromeos/cros/network_library.cc +++ b/chrome/browser/chromeos/cros/network_library.cc @@ -6,21 +6,14 @@ #include <algorithm> +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" -#include "net/url_request/url_request_job.h" - -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::NetworkLibraryImpl); namespace chromeos { -static const std::string kGoogleWifi = "Google"; -static const std::string kGoogleAWifi = "Google-A"; - // Helper function to wrap Html with <th> tag. static std::string WrapWithTH(std::string text) { return "<th>" + text + "</th>"; @@ -51,17 +44,18 @@ static std::string ToHtmlTableRow(Network* network) { if (network->type() == TYPE_WIFI || network->type() == TYPE_CELLULAR) { WirelessNetwork* wireless = static_cast<WirelessNetwork*>(network); str += WrapWithTD(wireless->name()) + - WrapWithTD(IntToString(wireless->auto_connect())) + - WrapWithTD(IntToString(wireless->strength())); + WrapWithTD(base::IntToString(wireless->auto_connect())) + + WrapWithTD(base::IntToString(wireless->strength())); if (network->type() == TYPE_WIFI) { WifiNetwork* wifi = static_cast<WifiNetwork*>(network); str += WrapWithTD(wifi->GetEncryptionString()) + - WrapWithTD(wifi->passphrase()) + WrapWithTD(wifi->identity()) + - WrapWithTD(wifi->cert_path()); + WrapWithTD(std::string(wifi->passphrase().length(), '*')) + + WrapWithTD(wifi->identity()) + WrapWithTD(wifi->cert_path()); } } str += WrapWithTD(network->GetStateString()) + - WrapWithTD(network->GetErrorString()) + WrapWithTD(network->ip_address()); + WrapWithTD(network->failed() ? network->GetErrorString() : "") + + WrapWithTD(network->ip_address()); return str; } @@ -123,7 +117,7 @@ std::string Network::GetStateString() { std::string Network::GetErrorString() { switch (error_) { case ERROR_UNKNOWN: - break; + return "Unknown Error"; case ERROR_OUT_OF_RANGE: return "Out Of Range"; case ERROR_PIN_MISSING: @@ -132,6 +126,10 @@ std::string Network::GetErrorString() { return "DHCP Failed"; case ERROR_CONNECT_FAILED: return "Connect Failed"; + case ERROR_BAD_PASSPHRASE: + return "Bad Passphrase"; + case ERROR_BAD_WEPKEY: + return "Bad WEP Key"; } return ""; } @@ -144,6 +142,7 @@ void WirelessNetwork::Clear() { name_.clear(); strength_ = 0; auto_connect_ = false; + favorite_ = false; } void WirelessNetwork::ConfigureFromService(const ServiceInfo& service) { @@ -151,17 +150,27 @@ void WirelessNetwork::ConfigureFromService(const ServiceInfo& service) { name_ = service.name; strength_ = service.strength; auto_connect_ = service.auto_connect; + favorite_ = service.favorite; } //////////////////////////////////////////////////////////////////////////////// // CellularNetwork + +bool CellularNetwork::StartActivation() const { + // TODO(ers, jglasgow): Kick of device activation process. + return true; +} + void CellularNetwork::Clear() { WirelessNetwork::Clear(); } void CellularNetwork::ConfigureFromService(const ServiceInfo& service) { WirelessNetwork::ConfigureFromService(service); + activation_state_ = service.activation_state; + // TODO(ers): Set other cellular properties here once they get added + // to ServiceInfo. } //////////////////////////////////////////////////////////////////////////////// @@ -203,622 +212,786 @@ std::string WifiNetwork::GetEncryptionString() { //////////////////////////////////////////////////////////////////////////////// // NetworkLibrary -// static -const int NetworkLibraryImpl::kNetworkTrafficeTimerSecs = 1; - -NetworkLibraryImpl::NetworkLibraryImpl() - : traffic_type_(0), - network_status_connection_(NULL), - available_devices_(0), - enabled_devices_(0), - connected_devices_(0), - offline_mode_(false) { - if (CrosLibrary::Get()->EnsureLoaded()) { - Init(); - } - g_url_request_job_tracker.AddObserver(this); -} +class NetworkLibraryImpl : public NetworkLibrary { + public: + NetworkLibraryImpl() + : network_status_connection_(NULL), + available_devices_(0), + enabled_devices_(0), + connected_devices_(0), + offline_mode_(false) { + if (CrosLibrary::Get()->EnsureLoaded()) { + Init(); + } else { + InitTestData(); + } + } -NetworkLibraryImpl::~NetworkLibraryImpl() { - if (network_status_connection_) { - DisconnectMonitorNetwork(network_status_connection_); + ~NetworkLibraryImpl() { + if (network_status_connection_) { + DisconnectMonitorNetwork(network_status_connection_); + } } - g_url_request_job_tracker.RemoveObserver(this); -} -//////////////////////////////////////////////////////////////////////////////// -// NetworkLibraryImpl, URLRequestJobTracker::JobObserver implementation: + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void NetworkLibraryImpl::OnJobAdded(URLRequestJob* job) { - CheckNetworkTraffic(false); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -void NetworkLibraryImpl::OnJobRemoved(URLRequestJob* job) { - CheckNetworkTraffic(false); -} + virtual const EthernetNetwork& ethernet_network() const { return ethernet_; } + virtual bool ethernet_connecting() const { return ethernet_.connecting(); } + virtual bool ethernet_connected() const { return ethernet_.connected(); } -void NetworkLibraryImpl::OnJobDone(URLRequestJob* job, - const URLRequestStatus& status) { - CheckNetworkTraffic(false); -} + virtual const std::string& wifi_name() const { return wifi_.name(); } + virtual bool wifi_connecting() const { return wifi_.connecting(); } + virtual bool wifi_connected() const { return wifi_.connected(); } + virtual int wifi_strength() const { return wifi_.strength(); } -void NetworkLibraryImpl::OnJobRedirect(URLRequestJob* job, const GURL& location, - int status_code) { - CheckNetworkTraffic(false); -} + virtual const std::string& cellular_name() const { return cellular_.name(); } + virtual bool cellular_connecting() const { return cellular_.connecting(); } + virtual bool cellular_connected() const { return cellular_.connected(); } + virtual int cellular_strength() const { return cellular_.strength(); } -void NetworkLibraryImpl::OnBytesRead(URLRequestJob* job, const char* buf, - int byte_count) { - CheckNetworkTraffic(true); -} + bool Connected() const { + return ethernet_connected() || wifi_connected() || cellular_connected(); + } -void NetworkLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + bool Connecting() const { + return ethernet_connecting() || wifi_connecting() || cellular_connecting(); + } -void NetworkLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + const std::string& IPAddress() const { + // Returns highest priority IP address. + if (ethernet_connected()) + return ethernet_.ip_address(); + if (wifi_connected()) + return wifi_.ip_address(); + if (cellular_connected()) + return cellular_.ip_address(); + return ethernet_.ip_address(); + } -//////////////////////////////////////////////////////////////////////////////// + virtual const WifiNetworkVector& wifi_networks() const { + return wifi_networks_; + } -bool NetworkLibraryImpl::FindWifiNetworkByPath( - const std::string& path, WifiNetwork* result) const { - const WifiNetwork* wifi = - GetWirelessNetworkByPath(wifi_networks_, path); - if (wifi) { - if (result) - *result = *wifi; - return true; + virtual const WifiNetworkVector& remembered_wifi_networks() const { + return remembered_wifi_networks_; } - return false; -} -bool NetworkLibraryImpl::FindCellularNetworkByPath( - const std::string& path, CellularNetwork* result) const { - const CellularNetwork* cellular = - GetWirelessNetworkByPath(cellular_networks_, path); - if (cellular) { - if (result) - *result = *cellular; - return true; + virtual const CellularNetworkVector& cellular_networks() const { + return cellular_networks_; } - return false; -} -void NetworkLibraryImpl::RequestWifiScan() { - if (CrosLibrary::Get()->EnsureLoaded()) { - RequestScan(TYPE_WIFI); + virtual const CellularNetworkVector& remembered_cellular_networks() const { + return remembered_cellular_networks_; } -} -bool NetworkLibraryImpl::GetWifiAccessPoints(WifiAccessPointVector* result) { - if (!CrosLibrary::Get()->EnsureLoaded()) - return false; - DeviceNetworkList* network_list = GetDeviceNetworkList(); - if (network_list == NULL) + ///////////////////////////////////////////////////////////////////////////// + + bool FindWifiNetworkByPath( + const std::string& path, WifiNetwork* result) const { + const WifiNetwork* wifi = + GetWirelessNetworkByPath(wifi_networks_, path); + if (wifi) { + if (result) + *result = *wifi; + return true; + } return false; - result->clear(); - result->reserve(network_list->network_size); - const base::Time now = base::Time::Now(); - for (size_t i = 0; i < network_list->network_size; ++i) { - DCHECK(network_list->networks[i].address); - DCHECK(network_list->networks[i].name); - WifiAccessPoint ap; - ap.mac_address = network_list->networks[i].address; - ap.name = network_list->networks[i].name; - ap.timestamp = now - - base::TimeDelta::FromSeconds(network_list->networks[i].age_seconds); - ap.signal_strength = network_list->networks[i].strength; - ap.channel = network_list->networks[i].channel; - result->push_back(ap); - } - FreeDeviceNetworkList(network_list); - return true; -} + } -bool NetworkLibraryImpl::ConnectToPreferredNetworkIfAvailable() { - // TODO(chocobo): Add the concept of preferred network to libcros. - // So that we don't have to hard-code Google-A here. - if (CrosLibrary::Get()->EnsureLoaded()) { - LOG(INFO) << "Attempting to auto-connect to Google wifi."; - // First force a refresh of the system info. - UpdateSystemInfo(); + bool FindCellularNetworkByPath( + const std::string& path, CellularNetwork* result) const { + const CellularNetwork* cellular = + GetWirelessNetworkByPath(cellular_networks_, path); + if (cellular) { + if (result) + *result = *cellular; + return true; + } + return false; + } - // If ethernet is connected, then don't bother. - if (ethernet_connected()) { - LOG(INFO) << "Ethernet connected, so don't need Google wifi."; - return false; + void RequestWifiScan() { + if (CrosLibrary::Get()->EnsureLoaded()) { + RequestScan(TYPE_WIFI); } + } - WifiNetwork* wifi = GetPreferredNetwork(); - if (!wifi) { - LOG(INFO) << "Google-A/Google wifi not found or set to not auto-connect."; + bool GetWifiAccessPoints(WifiAccessPointVector* result) { + if (!CrosLibrary::Get()->EnsureLoaded()) + return false; + DeviceNetworkList* network_list = GetDeviceNetworkList(); + if (network_list == NULL) return false; + result->clear(); + result->reserve(network_list->network_size); + const base::Time now = base::Time::Now(); + for (size_t i = 0; i < network_list->network_size; ++i) { + DCHECK(network_list->networks[i].address); + DCHECK(network_list->networks[i].name); + WifiAccessPoint ap; + ap.mac_address = network_list->networks[i].address; + ap.name = network_list->networks[i].name; + ap.timestamp = now - + base::TimeDelta::FromSeconds(network_list->networks[i].age_seconds); + ap.signal_strength = network_list->networks[i].strength; + ap.channel = network_list->networks[i].channel; + result->push_back(ap); } + FreeDeviceNetworkList(network_list); + return true; + } - // Save the wifi path, so we know which one we want to auto-connect to. - const std::string wifi_path = wifi->service_path(); - - // It takes some time for the enterprise daemon to start up and populate the - // certificate and identity. So we wait at most 3 seconds here. And every - // 100ms, we refetch the system info and check the cert and identify on the - // wifi. The enterprise daemon takes between 0.4 to 0.9 seconds to setup. - bool setup = false; - for (int i = 0; i < 30; i++) { - // Update the system and refetch the network. - UpdateSystemInfo(); - wifi = GetWirelessNetworkByPath(wifi_networks_, wifi_path); - // See if identity and certpath are available. - if (wifi && !wifi->identity().empty() && !wifi->cert_path().empty()) { - LOG(INFO) << "Google wifi set up after " << (i*0.1) << " seconds."; - setup = true; - break; + void ConnectToWifiNetwork(WifiNetwork network, + const std::string& password, + const std::string& identity, + const std::string& certpath) { + if (CrosLibrary::Get()->EnsureLoaded()) { + if (ConnectToNetworkWithCertInfo(network.service_path().c_str(), + password.empty() ? NULL : password.c_str(), + identity.empty() ? NULL : identity.c_str(), + certpath.empty() ? NULL : certpath.c_str())) { + // Update local cache and notify listeners. + WifiNetwork* wifi = GetWirelessNetworkByPath( + wifi_networks_, network.service_path()); + if (wifi) { + wifi->set_passphrase(password); + wifi->set_identity(identity); + wifi->set_cert_path(certpath); + wifi->set_connecting(true); + wifi_ = *wifi; + } + NotifyNetworkChanged(); } - PlatformThread::Sleep(100); } + } - if (!setup) { - LOG(INFO) << "Google wifi not set up after 3 seconds."; - return false; + void ConnectToWifiNetwork(const std::string& ssid, + const std::string& password, + const std::string& identity, + const std::string& certpath, + bool auto_connect) { + if (CrosLibrary::Get()->EnsureLoaded()) { + // First create a service from hidden network. + ServiceInfo* service = GetWifiService(ssid.c_str(), + SECURITY_UNKNOWN); + if (service) { + // Set auto-connect. + SetAutoConnect(service->service_path, auto_connect); + // Now connect to that service. + ConnectToNetworkWithCertInfo(service->service_path, + password.empty() ? NULL : password.c_str(), + identity.empty() ? NULL : identity.c_str(), + certpath.empty() ? NULL : certpath.c_str()); + + // Clean up ServiceInfo object. + FreeServiceInfo(service); + } else { + LOG(WARNING) << "Cannot find hidden network: " << ssid; + // TODO(chocobo): Show error message. + } } + } - // Now that we have a setup Google wifi, we can connect to it. - ConnectToNetwork(wifi_path.c_str(), NULL); - return true; + void ConnectToCellularNetwork(CellularNetwork network) { + if (CrosLibrary::Get()->EnsureLoaded()) { + if (ConnectToNetwork(network.service_path().c_str(), NULL)) { + // Update local cache and notify listeners. + CellularNetwork* cellular = GetWirelessNetworkByPath( + cellular_networks_, network.service_path()); + if (cellular) { + cellular->set_connecting(true); + cellular_ = *cellular; + } + NotifyNetworkChanged(); + } + } } - return false; -} -bool NetworkLibraryImpl::PreferredNetworkConnected() { - WifiNetwork* wifi = GetPreferredNetwork(); - return wifi && wifi->connected(); -} + void DisconnectFromWirelessNetwork(const WirelessNetwork& network) { + if (CrosLibrary::Get()->EnsureLoaded()) { + if (DisconnectFromNetwork(network.service_path().c_str())) { + // Update local cache and notify listeners. + if (network.type() == TYPE_WIFI) { + WifiNetwork* wifi = GetWirelessNetworkByPath( + wifi_networks_, network.service_path()); + if (wifi) { + wifi->set_connected(false); + wifi_ = WifiNetwork(); + } + } else if (network.type() == TYPE_CELLULAR) { + CellularNetwork* cellular = GetWirelessNetworkByPath( + cellular_networks_, network.service_path()); + if (cellular) { + cellular->set_connected(false); + cellular_ = CellularNetwork(); + } + } + NotifyNetworkChanged(); + } + } + } -bool NetworkLibraryImpl::PreferredNetworkFailed() { - WifiNetwork* wifi = GetPreferredNetwork(); - return !wifi || wifi->failed(); -} + void SaveCellularNetwork(const CellularNetwork& network) { + // Update the wifi network in the local cache. + CellularNetwork* cellular = GetWirelessNetworkByPath( + cellular_networks_, network.service_path()); + if (cellular) + *cellular = network; -void NetworkLibraryImpl::ConnectToWifiNetwork(WifiNetwork network, - const std::string& password, - const std::string& identity, - const std::string& certpath) { - if (CrosLibrary::Get()->EnsureLoaded()) { - ConnectToNetworkWithCertInfo(network.service_path().c_str(), - password.empty() ? NULL : password.c_str(), - identity.empty() ? NULL : identity.c_str(), - certpath.empty() ? NULL : certpath.c_str()); + // Update the cellular network with libcros. + if (CrosLibrary::Get()->EnsureLoaded()) { + SetAutoConnect(network.service_path().c_str(), network.auto_connect()); + } } -} -void NetworkLibraryImpl::ConnectToWifiNetwork(const std::string& ssid, - const std::string& password, - const std::string& identity, - const std::string& certpath, - bool auto_connect) { - if (CrosLibrary::Get()->EnsureLoaded()) { - // First create a service from hidden network. - ServiceInfo* service = GetWifiService(ssid.c_str(), - SECURITY_UNKNOWN); - if (service) { - // Set auto-connect. - SetAutoConnect(service->service_path, auto_connect); - // Now connect to that service. - ConnectToNetworkWithCertInfo(service->service_path, - password.empty() ? NULL : password.c_str(), - identity.empty() ? NULL : identity.c_str(), - certpath.empty() ? NULL : certpath.c_str()); - - // Clean up ServiceInfo object. - FreeServiceInfo(service); - } else { - LOG(WARNING) << "Cannot find hidden network: " << ssid; - // TODO(chocobo): Show error message. + void SaveWifiNetwork(const WifiNetwork& network) { + // Update the wifi network in the local cache. + WifiNetwork* wifi = GetWirelessNetworkByPath( + wifi_networks_, network.service_path()); + if (wifi) + *wifi = network; + + // Update the wifi network with libcros. + if (CrosLibrary::Get()->EnsureLoaded()) { + SetPassphrase( + network.service_path().c_str(), network.passphrase().c_str()); + SetIdentity(network.service_path().c_str(), network.identity().c_str()); + SetCertPath(network.service_path().c_str(), network.cert_path().c_str()); + SetAutoConnect(network.service_path().c_str(), network.auto_connect()); } } -} -void NetworkLibraryImpl::ConnectToCellularNetwork(CellularNetwork network) { - if (CrosLibrary::Get()->EnsureLoaded()) { - ConnectToNetwork(network.service_path().c_str(), NULL); + void ForgetWirelessNetwork(const std::string& service_path) { + if (CrosLibrary::Get()->EnsureLoaded()) { + if (DeleteRememberedService(service_path.c_str())) { + // Update local cache and notify listeners. + std::remove_if(remembered_wifi_networks_.begin(), + remembered_wifi_networks_.end(), + WirelessNetwork::ServicePathEq(service_path)); + std::remove_if(remembered_cellular_networks_.begin(), + remembered_cellular_networks_.end(), + WirelessNetwork::ServicePathEq(service_path)); + NotifyNetworkChanged(); + } + } } -} -void NetworkLibraryImpl::DisconnectFromWirelessNetwork( - const WirelessNetwork& network) { - if (CrosLibrary::Get()->EnsureLoaded()) { - DisconnectFromNetwork(network.service_path().c_str()); + virtual bool ethernet_available() const { + return available_devices_ & (1 << TYPE_ETHERNET); + } + virtual bool wifi_available() const { + return available_devices_ & (1 << TYPE_WIFI); + } + virtual bool cellular_available() const { + return available_devices_ & (1 << TYPE_CELLULAR); } -} - -void NetworkLibraryImpl::SaveCellularNetwork(const CellularNetwork& network) { - // Update the wifi network in the local cache. - CellularNetwork* cellular = GetWirelessNetworkByPath(cellular_networks_, - network.service_path()); - if (cellular) - *cellular = network; - // Update the cellular network with libcros. - if (CrosLibrary::Get()->EnsureLoaded()) { - SetAutoConnect(network.service_path().c_str(), network.auto_connect()); + virtual bool ethernet_enabled() const { + return enabled_devices_ & (1 << TYPE_ETHERNET); + } + virtual bool wifi_enabled() const { + return enabled_devices_ & (1 << TYPE_WIFI); + } + virtual bool cellular_enabled() const { + return enabled_devices_ & (1 << TYPE_CELLULAR); } -} -void NetworkLibraryImpl::SaveWifiNetwork(const WifiNetwork& network) { - // Update the wifi network in the local cache. - WifiNetwork* wifi = GetWirelessNetworkByPath(wifi_networks_, - network.service_path()); - if (wifi) - *wifi = network; + virtual bool offline_mode() const { return offline_mode_; } - // Update the wifi network with libcros. - if (CrosLibrary::Get()->EnsureLoaded()) { - SetPassphrase(network.service_path().c_str(), network.passphrase().c_str()); - SetIdentity(network.service_path().c_str(), network.identity().c_str()); - SetCertPath(network.service_path().c_str(), network.cert_path().c_str()); - SetAutoConnect(network.service_path().c_str(), network.auto_connect()); + void EnableEthernetNetworkDevice(bool enable) { + EnableNetworkDeviceType(TYPE_ETHERNET, enable); } -} -void NetworkLibraryImpl::ForgetWirelessNetwork(const WirelessNetwork& network) { - if (CrosLibrary::Get()->EnsureLoaded()) { - DeleteRememberedService(network.service_path().c_str()); + void EnableWifiNetworkDevice(bool enable) { + EnableNetworkDeviceType(TYPE_WIFI, enable); } -} - -void NetworkLibraryImpl::EnableEthernetNetworkDevice(bool enable) { - EnableNetworkDeviceType(TYPE_ETHERNET, enable); -} - -void NetworkLibraryImpl::EnableWifiNetworkDevice(bool enable) { - EnableNetworkDeviceType(TYPE_WIFI, enable); -} -void NetworkLibraryImpl::EnableCellularNetworkDevice(bool enable) { - EnableNetworkDeviceType(TYPE_CELLULAR, enable); -} + void EnableCellularNetworkDevice(bool enable) { + EnableNetworkDeviceType(TYPE_CELLULAR, enable); + } -void NetworkLibraryImpl::EnableOfflineMode(bool enable) { - if (!CrosLibrary::Get()->EnsureLoaded()) - return; + void EnableOfflineMode(bool enable) { + if (!CrosLibrary::Get()->EnsureLoaded()) + return; - // If network device is already enabled/disabled, then don't do anything. - if (enable && offline_mode_) { - LOG(INFO) << "Trying to enable offline mode when it's already enabled. "; - return; - } - if (!enable && !offline_mode_) { - LOG(INFO) << "Trying to disable offline mode when it's already disabled. "; - return; - } + // If network device is already enabled/disabled, then don't do anything. + if (enable && offline_mode_) { + LOG(INFO) << "Trying to enable offline mode when it's already enabled. "; + return; + } + if (!enable && !offline_mode_) { + LOG(INFO) << + "Trying to disable offline mode when it's already disabled. "; + return; + } - if (SetOfflineMode(enable)) { - offline_mode_ = enable; + if (SetOfflineMode(enable)) { + offline_mode_ = enable; + } } -} -NetworkIPConfigVector NetworkLibraryImpl::GetIPConfigs( - const std::string& device_path) { - NetworkIPConfigVector ipconfig_vector; - if (!device_path.empty()) { - IPConfigStatus* ipconfig_status = ListIPConfigs(device_path.c_str()); - if (ipconfig_status) { - for (int i = 0; i < ipconfig_status->size; i++) { - IPConfig ipconfig = ipconfig_status->ips[i]; - ipconfig_vector.push_back( - NetworkIPConfig(device_path, ipconfig.type, ipconfig.address, - ipconfig.netmask, ipconfig.gateway, - ipconfig.name_servers)); + NetworkIPConfigVector GetIPConfigs(const std::string& device_path) { + NetworkIPConfigVector ipconfig_vector; + if (!device_path.empty()) { + IPConfigStatus* ipconfig_status = ListIPConfigs(device_path.c_str()); + if (ipconfig_status) { + for (int i = 0; i < ipconfig_status->size; i++) { + IPConfig ipconfig = ipconfig_status->ips[i]; + ipconfig_vector.push_back( + NetworkIPConfig(device_path, ipconfig.type, ipconfig.address, + ipconfig.netmask, ipconfig.gateway, + ipconfig.name_servers)); + } + FreeIPConfigStatus(ipconfig_status); + // Sort the list of ip configs by type. + std::sort(ipconfig_vector.begin(), ipconfig_vector.end()); } - FreeIPConfigStatus(ipconfig_status); - // Sort the list of ip configs by type. - std::sort(ipconfig_vector.begin(), ipconfig_vector.end()); } - } - return ipconfig_vector; -} + return ipconfig_vector; + } + + std::string GetHtmlInfo(int refresh) { + std::string output; + output.append("<html><head><title>About Network</title>"); + if (refresh > 0) + output.append("<meta http-equiv=\"refresh\" content=\"" + + base::IntToString(refresh) + "\"/>"); + output.append("</head><body>"); + if (refresh > 0) { + output.append("(Auto-refreshing page every " + + base::IntToString(refresh) + "s)"); + } else { + output.append("(To auto-refresh this page: about:network/<secs>)"); + } -std::string NetworkLibraryImpl::GetHtmlInfo(int refresh) { - std::string output; - output.append("<html><head><title>About Network</title>"); - if (refresh > 0) - output.append("<meta http-equiv=\"refresh\" content=\"" + - IntToString(refresh) + "\"/>"); - output.append("</head><body>"); - if (refresh > 0) - output.append("(Auto-refreshing page every " + IntToString(refresh) + "s)"); - else - output.append("(To auto-refresh this page: about:network/<secs>)"); + output.append("<h3>Ethernet:</h3><table border=1>"); + if (ethernet_enabled()) { + output.append("<tr>" + ToHtmlTableHeader(ðernet_) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(ðernet_) + "</tr>"); + } - output.append("<h3>Ethernet:</h3><table border=1>"); - output.append("<tr>" + ToHtmlTableHeader(ðernet_) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(ðernet_) + "</tr>"); + output.append("</table><h3>Wifi:</h3><table border=1>"); + for (size_t i = 0; i < wifi_networks_.size(); ++i) { + if (i == 0) + output.append("<tr>" + ToHtmlTableHeader(&wifi_networks_[i]) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(&wifi_networks_[i]) + "</tr>"); + } - output.append("</table><h3>Wifi:</h3><table border=1>"); - for (size_t i = 0; i < wifi_networks_.size(); ++i) { - if (i == 0) - output.append("<tr>" + ToHtmlTableHeader(&wifi_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&wifi_networks_[i]) + "</tr>"); - } + output.append("</table><h3>Cellular:</h3><table border=1>"); + for (size_t i = 0; i < cellular_networks_.size(); ++i) { + if (i == 0) + output.append("<tr>" + ToHtmlTableHeader(&cellular_networks_[i]) + + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(&cellular_networks_[i]) + "</tr>"); + } - output.append("</table><h3>Cellular:</h3><table border=1>"); - for (size_t i = 0; i < cellular_networks_.size(); ++i) { - if (i == 0) - output.append("<tr>" + ToHtmlTableHeader(&cellular_networks_[i]) + + output.append("</table><h3>Remembered Wifi:</h3><table border=1>"); + for (size_t i = 0; i < remembered_wifi_networks_.size(); ++i) { + if (i == 0) + output.append( + "<tr>" + ToHtmlTableHeader(&remembered_wifi_networks_[i]) + + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(&remembered_wifi_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&cellular_networks_[i]) + "</tr>"); - } + } - output.append("</table><h3>Remembered Wifi:</h3><table border=1>"); - for (size_t i = 0; i < remembered_wifi_networks_.size(); ++i) { - if (i == 0) - output.append("<tr>" + ToHtmlTableHeader(&remembered_wifi_networks_[i]) + + output.append("</table><h3>Remembered Cellular:</h3><table border=1>"); + for (size_t i = 0; i < remembered_cellular_networks_.size(); ++i) { + if (i == 0) + output.append("<tr>" + + ToHtmlTableHeader(&remembered_cellular_networks_[i]) + "</tr>"); + output.append("<tr>" + ToHtmlTableRow(&remembered_cellular_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&remembered_wifi_networks_[i]) + - "</tr>"); - } + } - output.append("</table><h3>Remembered Cellular:</h3><table border=1>"); - for (size_t i = 0; i < remembered_cellular_networks_.size(); ++i) { - if (i == 0) - output.append("<tr>" + - ToHtmlTableHeader(&remembered_cellular_networks_[i]) + "</tr>"); - output.append("<tr>" + ToHtmlTableRow(&remembered_cellular_networks_[i]) + - "</tr>"); + output.append("</table></body></html>"); + return output; } - output.append("</table></body></html>"); - return output; -} - -// static -void NetworkLibraryImpl::NetworkStatusChangedHandler(void* object) { - NetworkLibraryImpl* network = static_cast<NetworkLibraryImpl*>(object); - DCHECK(network); - network->UpdateNetworkStatus(); -} + private: + static void NetworkStatusChangedHandler(void* object) { + NetworkLibraryImpl* network = static_cast<NetworkLibraryImpl*>(object); + DCHECK(network); + network->UpdateNetworkStatus(); + } -// static -void NetworkLibraryImpl::ParseSystem(SystemInfo* system, - EthernetNetwork* ethernet, - WifiNetworkVector* wifi_networks, - CellularNetworkVector* cellular_networks, - WifiNetworkVector* remembered_wifi_networks, - CellularNetworkVector* remembered_cellular_networks) { - DLOG(INFO) << "ParseSystem:"; - ethernet->Clear(); - for (int i = 0; i < system->service_size; i++) { - const ServiceInfo& service = system->services[i]; - DLOG(INFO) << " (" << service.type << - ") " << service.name << - " mode=" << service.mode << - " state=" << service.state << - " sec=" << service.security << - " req=" << service.passphrase_required << - " pass=" << service.passphrase << - " id=" << service.identity << - " certpath=" << service.cert_path << - " str=" << service.strength << - " fav=" << service.favorite << - " auto=" << service.auto_connect << - " error=" << service.error; - // Once a connected ethernet service is found, disregard other ethernet - // services that are also found - if (service.type == TYPE_ETHERNET && !(ethernet->connected())) - ethernet->ConfigureFromService(service); - else if (service.type == TYPE_WIFI) - wifi_networks->push_back(WifiNetwork(service)); - else if (service.type == TYPE_CELLULAR) - cellular_networks->push_back(CellularNetwork(service)); - } - DLOG(INFO) << "Remembered networks:"; - for (int i = 0; i < system->remembered_service_size; i++) { - const ServiceInfo& service = system->remembered_services[i]; - // Only serices marked as auto_connect are considered remembered networks. - // TODO(chocobo): Don't add to remembered service if currently available. - if (service.auto_connect) { + static void ParseSystem(SystemInfo* system, + EthernetNetwork* ethernet, + WifiNetworkVector* wifi_networks, + CellularNetworkVector* cellular_networks, + WifiNetworkVector* remembered_wifi_networks, + CellularNetworkVector* remembered_cellular_networks) { + DLOG(INFO) << "ParseSystem:"; + ethernet->Clear(); + for (int i = 0; i < system->service_size; i++) { + const ServiceInfo service = *system->GetServiceInfo(i); DLOG(INFO) << " (" << service.type << ") " << service.name << " mode=" << service.mode << + " state=" << service.state << " sec=" << service.security << + " req=" << service.passphrase_required << " pass=" << service.passphrase << " id=" << service.identity << " certpath=" << service.cert_path << - " auto=" << service.auto_connect; - if (service.type == TYPE_WIFI) - remembered_wifi_networks->push_back(WifiNetwork(service)); + " str=" << service.strength << + " fav=" << service.favorite << + " auto=" << service.auto_connect << + " error=" << service.error; + // Once a connected ethernet service is found, disregard other ethernet + // services that are also found + if (service.type == TYPE_ETHERNET && !(ethernet->connected())) + ethernet->ConfigureFromService(service); + else if (service.type == TYPE_WIFI) + wifi_networks->push_back(WifiNetwork(service)); else if (service.type == TYPE_CELLULAR) - remembered_cellular_networks->push_back(CellularNetwork(service)); + cellular_networks->push_back(CellularNetwork(service)); + } + DLOG(INFO) << "Remembered networks:"; + for (int i = 0; i < system->remembered_service_size; i++) { + const ServiceInfo& service = *system->GetRememberedServiceInfo(i); + // Only serices marked as auto_connect are considered remembered networks. + // TODO(chocobo): Don't add to remembered service if currently available. + if (service.auto_connect) { + DLOG(INFO) << " (" << service.type << + ") " << service.name << + " mode=" << service.mode << + " sec=" << service.security << + " pass=" << service.passphrase << + " id=" << service.identity << + " certpath=" << service.cert_path << + " auto=" << service.auto_connect; + if (service.type == TYPE_WIFI) + remembered_wifi_networks->push_back(WifiNetwork(service)); + else if (service.type == TYPE_CELLULAR) + remembered_cellular_networks->push_back(CellularNetwork(service)); + } } } -} - -void NetworkLibraryImpl::Init() { - // First, get the currently available networks. This data is cached - // on the connman side, so the call should be quick. - LOG(INFO) << "Getting initial CrOS network info."; - UpdateSystemInfo(); - LOG(INFO) << "Registering for network status updates."; - // Now, register to receive updates on network status. - network_status_connection_ = MonitorNetwork(&NetworkStatusChangedHandler, - this); -} + void Init() { + // First, get the currently available networks. This data is cached + // on the connman side, so the call should be quick. + LOG(INFO) << "Getting initial CrOS network info."; + UpdateSystemInfo(); -void NetworkLibraryImpl::UpdateSystemInfo() { - if (CrosLibrary::Get()->EnsureLoaded()) { - UpdateNetworkStatus(); + LOG(INFO) << "Registering for network status updates."; + // Now, register to receive updates on network status. + network_status_connection_ = MonitorNetwork(&NetworkStatusChangedHandler, + this); + } + + void InitTestData() { + ethernet_.Clear(); + ethernet_.set_connected(true); + + wifi_networks_.clear(); + WifiNetwork wifi1 = WifiNetwork(); + wifi1.set_service_path("fw1"); + wifi1.set_name("Fake Wifi 1"); + wifi1.set_strength(90); + wifi1.set_connected(false); + wifi1.set_encryption(SECURITY_NONE); + wifi_networks_.push_back(wifi1); + + WifiNetwork wifi2 = WifiNetwork(); + wifi2.set_service_path("fw2"); + wifi2.set_name("Fake Wifi 2"); + wifi2.set_strength(70); + wifi2.set_connected(true); + wifi2.set_encryption(SECURITY_WEP); + wifi_networks_.push_back(wifi2); + + WifiNetwork wifi3 = WifiNetwork(); + wifi3.set_service_path("fw3"); + wifi3.set_name("Fake Wifi 3"); + wifi3.set_strength(50); + wifi3.set_connected(false); + wifi3.set_encryption(SECURITY_WEP); + wifi_networks_.push_back(wifi3); + + wifi_ = wifi2; + + cellular_networks_.clear(); + + cellular_networks_.clear(); + CellularNetwork cellular1 = CellularNetwork(); + cellular1.set_service_path("fc1"); + cellular1.set_name("Fake Cellular 1"); + cellular1.set_strength(90); + cellular1.set_connected(false); + cellular_networks_.push_back(cellular1); + + CellularNetwork cellular2 = CellularNetwork(); + cellular2.set_service_path("fc2"); + cellular2.set_name("Fake Cellular 2"); + cellular2.set_strength(70); + cellular2.set_connected(true); + cellular_networks_.push_back(cellular2); + + CellularNetwork cellular3 = CellularNetwork(); + cellular3.set_service_path("fc3"); + cellular3.set_name("Fake Cellular 3"); + cellular3.set_strength(50); + cellular3.set_connected(false); + cellular_networks_.push_back(cellular3); + + cellular_ = cellular2; + + remembered_wifi_networks_.clear(); + remembered_wifi_networks_.push_back(wifi2); + + remembered_cellular_networks_.clear(); + remembered_cellular_networks_.push_back(cellular2); + + int devices = (1 << TYPE_ETHERNET) | (1 << TYPE_WIFI) | + (1 << TYPE_CELLULAR); + available_devices_ = devices; + enabled_devices_ = devices; + connected_devices_ = devices; + offline_mode_ = false; + } + + void UpdateSystemInfo() { + if (CrosLibrary::Get()->EnsureLoaded()) { + UpdateNetworkStatus(); + } } -} - -WifiNetwork* NetworkLibraryImpl::GetPreferredNetwork() { - // First look for Google-A then look for Google. - // Only care if set to auto-connect. - WifiNetwork* wifi = GetWifiNetworkByName(kGoogleAWifi); - // If wifi found and set to not auto-connect, then ignore it. - if (wifi && !wifi->auto_connect()) - wifi = NULL; - - if (!wifi) { - wifi = GetWifiNetworkByName(kGoogleWifi); - // If wifi found and set to not auto-connect, then ignore it. - if (wifi && !wifi->auto_connect()) - wifi = NULL; - } - return wifi; -} -WifiNetwork* NetworkLibraryImpl::GetWifiNetworkByName(const std::string& name) { - for (size_t i = 0; i < wifi_networks_.size(); ++i) { - if (wifi_networks_[i].name().compare(name) == 0) { - return &wifi_networks_[i]; + WifiNetwork* GetWifiNetworkByName(const std::string& name) { + for (size_t i = 0; i < wifi_networks_.size(); ++i) { + if (wifi_networks_[i].name().compare(name) == 0) { + return &wifi_networks_[i]; + } } + return NULL; } - return NULL; -} - -template<typename T> T* NetworkLibraryImpl::GetWirelessNetworkByPath( - std::vector<T>& networks, const std::string& path) { - typedef typename std::vector<T>::iterator iter_t; - iter_t iter = std::find_if(networks.begin(), networks.end(), - WirelessNetwork::ServicePathEq(path)); - return (iter != networks.end()) ? &(*iter) : NULL; -} -// const version -template<typename T> const T* NetworkLibraryImpl::GetWirelessNetworkByPath( - const std::vector<T>& networks, const std::string& path) const { - typedef typename std::vector<T>::const_iterator iter_t; - iter_t iter = std::find_if(networks.begin(), networks.end(), - WirelessNetwork::ServicePathEq(path)); - return (iter != networks.end()) ? &(*iter) : NULL; -} - -void NetworkLibraryImpl::EnableNetworkDeviceType(ConnectionType device, - bool enable) { - if (!CrosLibrary::Get()->EnsureLoaded()) - return; - - // If network device is already enabled/disabled, then don't do anything. - if (enable && (enabled_devices_ & (1 << device))) { - LOG(WARNING) << "Trying to enable a device that's already enabled: " - << device; - return; + template<typename T> T* GetWirelessNetworkByPath( + std::vector<T>& networks, const std::string& path) { + typedef typename std::vector<T>::iterator iter_t; + iter_t iter = std::find_if(networks.begin(), networks.end(), + WirelessNetwork::ServicePathEq(path)); + return (iter != networks.end()) ? &(*iter) : NULL; } - if (!enable && !(enabled_devices_ & (1 << device))) { - LOG(WARNING) << "Trying to disable a device that's already disabled: " - << device; - return; - } - - EnableNetworkDevice(device, enable); -} -void NetworkLibraryImpl::UpdateNetworkStatus() { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, - &NetworkLibraryImpl::UpdateNetworkStatus)); - return; + // const version + template<typename T> const T* GetWirelessNetworkByPath( + const std::vector<T>& networks, const std::string& path) const { + typedef typename std::vector<T>::const_iterator iter_t; + iter_t iter = std::find_if(networks.begin(), networks.end(), + WirelessNetwork::ServicePathEq(path)); + return (iter != networks.end()) ? &(*iter) : NULL; } - SystemInfo* system = GetSystemInfo(); - if (!system) - return; - - wifi_networks_.clear(); - cellular_networks_.clear(); - remembered_wifi_networks_.clear(); - remembered_cellular_networks_.clear(); - ParseSystem(system, ðernet_, &wifi_networks_, &cellular_networks_, - &remembered_wifi_networks_, &remembered_cellular_networks_); + void EnableNetworkDeviceType(ConnectionType device, bool enable) { + if (!CrosLibrary::Get()->EnsureLoaded()) + return; - wifi_ = WifiNetwork(); - for (size_t i = 0; i < wifi_networks_.size(); i++) { - if (wifi_networks_[i].connecting_or_connected()) { - wifi_ = wifi_networks_[i]; - break; // There is only one connected or connecting wifi network. + // If network device is already enabled/disabled, then don't do anything. + if (enable && (enabled_devices_ & (1 << device))) { + LOG(WARNING) << "Trying to enable a device that's already enabled: " + << device; + return; } - } - cellular_ = CellularNetwork(); - for (size_t i = 0; i < cellular_networks_.size(); i++) { - if (cellular_networks_[i].connecting_or_connected()) { - cellular_ = cellular_networks_[i]; - break; // There is only one connected or connecting cellular network. + if (!enable && !(enabled_devices_ & (1 << device))) { + LOG(WARNING) << "Trying to disable a device that's already disabled: " + << device; + return; } + + EnableNetworkDevice(device, enable); } - available_devices_ = system->available_technologies; - enabled_devices_ = system->enabled_technologies; - connected_devices_ = system->connected_technologies; - offline_mode_ = system->offline_mode; + void NotifyNetworkChanged() { + FOR_EACH_OBSERVER(Observer, observers_, NetworkChanged(this)); + } - FOR_EACH_OBSERVER(Observer, observers_, NetworkChanged(this)); - FreeSystemInfo(system); -} + void UpdateNetworkStatus() { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &NetworkLibraryImpl::UpdateNetworkStatus)); + return; + } -void NetworkLibraryImpl::CheckNetworkTraffic(bool download) { - // If we already have a pending upload and download notification, then - // shortcut and return. - if (traffic_type_ == (Observer::TRAFFIC_DOWNLOAD | Observer::TRAFFIC_UPLOAD)) - return; - // Figure out if we are uploading and/or downloading. We are downloading - // if download == true. We are uploading if we have upload progress. - if (download) - traffic_type_ |= Observer::TRAFFIC_DOWNLOAD; - if ((traffic_type_ & Observer::TRAFFIC_UPLOAD) == 0) { - URLRequestJobTracker::JobIterator it; - for (it = g_url_request_job_tracker.begin(); - it != g_url_request_job_tracker.end(); - ++it) { - URLRequestJob* job = *it; - if (job->GetUploadProgress() > 0) { - traffic_type_ |= Observer::TRAFFIC_UPLOAD; - break; + SystemInfo* system = GetSystemInfo(); + if (!system) + return; + + wifi_networks_.clear(); + cellular_networks_.clear(); + remembered_wifi_networks_.clear(); + remembered_cellular_networks_.clear(); + ParseSystem(system, ðernet_, &wifi_networks_, &cellular_networks_, + &remembered_wifi_networks_, &remembered_cellular_networks_); + + wifi_ = WifiNetwork(); + for (size_t i = 0; i < wifi_networks_.size(); i++) { + if (wifi_networks_[i].connecting_or_connected()) { + wifi_ = wifi_networks_[i]; + break; // There is only one connected or connecting wifi network. + } + } + cellular_ = CellularNetwork(); + for (size_t i = 0; i < cellular_networks_.size(); i++) { + if (cellular_networks_[i].connecting_or_connected()) { + cellular_ = cellular_networks_[i]; + break; // There is only one connected or connecting cellular network. } } - } - // If we have new traffic data to send out and the timer is not currently - // running, then start a new timer. - if (traffic_type_ && !timer_.IsRunning()) { - timer_.Start(base::TimeDelta::FromSeconds(kNetworkTrafficeTimerSecs), this, - &NetworkLibraryImpl::NetworkTrafficTimerFired); - } -} -void NetworkLibraryImpl:: NetworkTrafficTimerFired() { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &NetworkLibraryImpl::NotifyNetworkTraffic, - traffic_type_)); - // Reset traffic type so that we don't send the same data next time. - traffic_type_ = 0; -} + available_devices_ = system->available_technologies; + enabled_devices_ = system->enabled_technologies; + connected_devices_ = system->connected_technologies; + offline_mode_ = system->offline_mode; -void NetworkLibraryImpl::NotifyNetworkTraffic(int traffic_type) { - FOR_EACH_OBSERVER(Observer, observers_, NetworkTraffic(this, traffic_type)); -} + NotifyNetworkChanged(); + FreeSystemInfo(system); + } -bool NetworkLibraryImpl::Connected() const { - return ethernet_connected() || wifi_connected() || cellular_connected(); -} + ObserverList<Observer> observers_; -bool NetworkLibraryImpl::Connecting() const { - return ethernet_connecting() || wifi_connecting() || cellular_connecting(); -} + // The network status connection for monitoring network status changes. + MonitorNetworkConnection network_status_connection_; -const std::string& NetworkLibraryImpl::IPAddress() const { - // Returns highest priority IP address. - if (ethernet_connected()) - return ethernet_.ip_address(); - if (wifi_connected()) - return wifi_.ip_address(); - if (cellular_connected()) - return cellular_.ip_address(); - return ethernet_.ip_address(); + // The ethernet network. + EthernetNetwork ethernet_; + + // The list of available wifi networks. + WifiNetworkVector wifi_networks_; + + // The current connected (or connecting) wifi network. + WifiNetwork wifi_; + + // The remembered wifi networks. + WifiNetworkVector remembered_wifi_networks_; + + // The list of available cellular networks. + CellularNetworkVector cellular_networks_; + + // The current connected (or connecting) cellular network. + CellularNetwork cellular_; + + // The remembered cellular networks. + CellularNetworkVector remembered_cellular_networks_; + + // The current available network devices. Bitwise flag of ConnectionTypes. + int available_devices_; + + // The current enabled network devices. Bitwise flag of ConnectionTypes. + int enabled_devices_; + + // The current connected network devices. Bitwise flag of ConnectionTypes. + int connected_devices_; + + bool offline_mode_; + + DISALLOW_COPY_AND_ASSIGN(NetworkLibraryImpl); +}; + +class NetworkLibraryStubImpl : public NetworkLibrary { + public: + NetworkLibraryStubImpl() : ip_address_("1.1.1.1") {} + ~NetworkLibraryStubImpl() {} + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + virtual const EthernetNetwork& ethernet_network() const { + return ethernet_; + } + virtual bool ethernet_connecting() const { return false; } + virtual bool ethernet_connected() const { return true; } + virtual const std::string& wifi_name() const { return EmptyString(); } + virtual bool wifi_connecting() const { return false; } + virtual bool wifi_connected() const { return false; } + virtual int wifi_strength() const { return 0; } + + virtual const std::string& cellular_name() const { return EmptyString(); } + virtual bool cellular_connecting() const { return false; } + virtual bool cellular_connected() const { return false; } + virtual int cellular_strength() const { return false; } + + bool Connected() const { return true; } + bool Connecting() const { return false; } + const std::string& IPAddress() const { return ip_address_; } + virtual const WifiNetworkVector& wifi_networks() const { + return wifi_networks_; + } + virtual const WifiNetworkVector& remembered_wifi_networks() const { + return wifi_networks_; + } + virtual const CellularNetworkVector& cellular_networks() const { + return cellular_networks_; + } + virtual const CellularNetworkVector& remembered_cellular_networks() const { + return cellular_networks_; + } + + ///////////////////////////////////////////////////////////////////////////// + + bool FindWifiNetworkByPath( + const std::string& path, WifiNetwork* result) const { return false; } + bool FindCellularNetworkByPath( + const std::string& path, CellularNetwork* result) const { return false; } + void RequestWifiScan() {} + bool GetWifiAccessPoints(WifiAccessPointVector* result) { return false; } + + void ConnectToWifiNetwork(WifiNetwork network, + const std::string& password, + const std::string& identity, + const std::string& certpath) {} + void ConnectToWifiNetwork(const std::string& ssid, + const std::string& password, + const std::string& identity, + const std::string& certpath, + bool auto_connect) {} + void ConnectToCellularNetwork(CellularNetwork network) {} + void DisconnectFromWirelessNetwork(const WirelessNetwork& network) {} + void SaveCellularNetwork(const CellularNetwork& network) {} + void SaveWifiNetwork(const WifiNetwork& network) {} + void ForgetWirelessNetwork(const std::string& service_path) {} + virtual bool ethernet_available() const { return true; } + virtual bool wifi_available() const { return false; } + virtual bool cellular_available() const { return false; } + virtual bool ethernet_enabled() const { return true; } + virtual bool wifi_enabled() const { return false; } + virtual bool cellular_enabled() const { return false; } + virtual bool offline_mode() const { return false; } + void EnableEthernetNetworkDevice(bool enable) {} + void EnableWifiNetworkDevice(bool enable) {} + void EnableCellularNetworkDevice(bool enable) {} + void EnableOfflineMode(bool enable) {} + NetworkIPConfigVector GetIPConfigs(const std::string& device_path) { + return NetworkIPConfigVector(); + } + std::string GetHtmlInfo(int refresh) { return std::string(); } + void UpdateSystemInfo() {} + + private: + std::string ip_address_; + EthernetNetwork ethernet_; + WifiNetworkVector wifi_networks_; + CellularNetworkVector cellular_networks_; +}; + +// static +NetworkLibrary* NetworkLibrary::GetImpl(bool stub) { + if (stub) + return new NetworkLibraryStubImpl(); + else + return new NetworkLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::NetworkLibraryImpl); diff --git a/chrome/browser/chromeos/cros/network_library.h b/chrome/browser/chromeos/cros/network_library.h index 9521a41..c10cc01 100644 --- a/chrome/browser/chromeos/cros/network_library.h +++ b/chrome/browser/chromeos/cros/network_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_NETWORK_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_NETWORK_LIBRARY_H_ +#pragma once #include <string> #include <vector> @@ -11,9 +12,7 @@ #include "base/observer_list.h" #include "base/platform_thread.h" #include "base/singleton.h" -#include "base/string16.h" #include "base/timer.h" -#include "net/url_request/url_request_job_tracker.h" #include "cros/chromeos_network.h" namespace chromeos { @@ -31,6 +30,8 @@ class Network { bool failed() const { return state_ == STATE_FAILURE; } ConnectionError error() const { return error_; } + void set_service_path(const std::string& service_path) { + service_path_ = service_path; } void set_connecting(bool connecting) { state_ = (connecting ? STATE_ASSOCIATION : STATE_IDLE); } void set_connected(bool connected) { state_ = (connected ? @@ -89,9 +90,12 @@ class WirelessNetwork : public Network { const std::string& name() const { return name_; } int strength() const { return strength_; } bool auto_connect() const { return auto_connect_; } + bool favorite() const { return favorite_; } void set_name(const std::string& name) { name_ = name; } + void set_strength(int strength) { strength_ = strength; } void set_auto_connect(bool auto_connect) { auto_connect_ = auto_connect; } + void set_favorite(bool favorite) { favorite_ = favorite; } // Network overrides. virtual void Clear(); @@ -101,11 +105,13 @@ class WirelessNetwork : public Network { WirelessNetwork() : Network(), strength_(0), - auto_connect_(false) {} + auto_connect_(false), + favorite_(false) {} std::string name_; int strength_; bool auto_connect_; + bool favorite_; }; class CellularNetwork : public WirelessNetwork { @@ -115,10 +121,29 @@ class CellularNetwork : public WirelessNetwork { : WirelessNetwork() { ConfigureFromService(service); } + // Starts device activation process. Returns false if the device state does + // not permit activation. + bool StartActivation() const; + const ActivationState activation_state() const { return activation_state_; } + const std::string& payment_url() const { return payment_url_; } + const std::string& meid() const { return meid_; } + const std::string& imei() const { return imei_; } + const std::string& imsi() const { return imsi_; } + const std::string& esn() const { return esn_; } + const std::string& mdn() const { return mdn_; } // WirelessNetwork overrides. virtual void Clear(); virtual void ConfigureFromService(const ServiceInfo& service); + + protected: + ActivationState activation_state_; + std::string payment_url_; + std::string meid_; + std::string imei_; + std::string imsi_; + std::string esn_; + std::string mdn_; }; class WifiNetwork : public WirelessNetwork { @@ -221,22 +246,15 @@ struct NetworkIPConfig { }; typedef std::vector<NetworkIPConfig> NetworkIPConfigVector; +// This class handles the interaction with the ChromeOS network library APIs. +// Classes can add themselves as observers. Users can get an instance of the +// library like this: chromeos::CrosLibrary::Get()->GetNetworkLibrary() class NetworkLibrary { public: class Observer { public: - // A bitfield mask for traffic types. - enum TrafficTypes { - TRAFFIC_DOWNLOAD = 0x1, - TRAFFIC_UPLOAD = 0x2, - } TrafficTypeMasks; - // Called when the network has changed. (wifi networks, and ethernet) virtual void NetworkChanged(NetworkLibrary* obj) = 0; - - // Called when network traffic has been detected. - // Takes a bitfield of TrafficTypeMasks. - virtual void NetworkTraffic(NetworkLibrary* obj, int traffic_type) = 0; }; virtual ~NetworkLibrary() {} @@ -301,18 +319,6 @@ class NetworkLibrary { // Force an update of the system info. virtual void UpdateSystemInfo() = 0; - // Attempt to connect to the preferred network if available and it is set up. - // This call will return true if connection is started. - // If the preferred network is not available or not setup, returns false. - // Note: For dogfood purposes, we hardcode the preferred network to Google-A. - virtual bool ConnectToPreferredNetworkIfAvailable() = 0; - - // Returns true if we are currently connected to the preferred network. - virtual bool PreferredNetworkConnected() = 0; - - // Returns true if we failed to connect to the preferred network. - virtual bool PreferredNetworkFailed() = 0; - // Connect to the specified wireless network with password. virtual void ConnectToWifiNetwork(WifiNetwork network, const std::string& password, @@ -338,7 +344,7 @@ class NetworkLibrary { virtual void SaveWifiNetwork(const WifiNetwork& network) = 0; // Forget the passed in wireless (either cellular or wifi) network. - virtual void ForgetWirelessNetwork(const WirelessNetwork& network) = 0; + virtual void ForgetWirelessNetwork(const std::string& service_path) = 0; virtual bool ethernet_available() const = 0; virtual bool wifi_available() const = 0; @@ -369,229 +375,10 @@ class NetworkLibrary { // Fetches debug network info for display in about:network. // The page will have a meta refresh of |refresh| seconds if |refresh| > 0. virtual std::string GetHtmlInfo(int refresh) = 0; -}; - -// This class handles the interaction with the ChromeOS network library APIs. -// Classes can add themselves as observers. Users can get an instance of this -// library class like this: NetworkLibrary::Get() -class NetworkLibraryImpl : public NetworkLibrary, - public URLRequestJobTracker::JobObserver { - public: - NetworkLibraryImpl(); - virtual ~NetworkLibraryImpl(); - - // URLRequestJobTracker::JobObserver methods (called on the IO thread): - virtual void OnJobAdded(URLRequestJob* job); - virtual void OnJobRemoved(URLRequestJob* job); - virtual void OnJobDone(URLRequestJob* job, const URLRequestStatus& status); - virtual void OnJobRedirect(URLRequestJob* job, const GURL& location, - int status_code); - virtual void OnBytesRead(URLRequestJob* job, const char* buf, int byte_count); - - // NetworkLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - - virtual const EthernetNetwork& ethernet_network() const { return ethernet_; } - virtual bool ethernet_connecting() const { return ethernet_.connecting(); } - virtual bool ethernet_connected() const { return ethernet_.connected(); } - - virtual const std::string& wifi_name() const { return wifi_.name(); } - virtual bool wifi_connecting() const { return wifi_.connecting(); } - virtual bool wifi_connected() const { return wifi_.connected(); } - virtual int wifi_strength() const { return wifi_.strength(); } - - virtual const std::string& cellular_name() const { return cellular_.name(); } - virtual bool cellular_connecting() const { return cellular_.connecting(); } - virtual bool cellular_connected() const { return cellular_.connected(); } - virtual int cellular_strength() const { return cellular_.strength(); } - - virtual bool Connected() const; - virtual bool Connecting() const; - virtual const std::string& IPAddress() const; - - virtual const WifiNetworkVector& wifi_networks() const { - return wifi_networks_; - } - - virtual const WifiNetworkVector& remembered_wifi_networks() const { - return remembered_wifi_networks_; - } - - virtual const CellularNetworkVector& cellular_networks() const { - return cellular_networks_; - } - - virtual const CellularNetworkVector& remembered_cellular_networks() const { - return remembered_cellular_networks_; - } - - virtual bool FindWifiNetworkByPath(const std::string& path, - WifiNetwork* network) const; - virtual bool FindCellularNetworkByPath(const std::string& path, - CellularNetwork* network) const; - - virtual void RequestWifiScan(); - virtual bool GetWifiAccessPoints(WifiAccessPointVector* result); - virtual bool ConnectToPreferredNetworkIfAvailable(); - virtual bool PreferredNetworkConnected(); - virtual bool PreferredNetworkFailed(); - virtual void ConnectToWifiNetwork(WifiNetwork network, - const std::string& password, - const std::string& identity, - const std::string& certpath); - virtual void ConnectToWifiNetwork(const std::string& ssid, - const std::string& password, - const std::string& identity, - const std::string& certpath, - bool auto_connect); - virtual void ConnectToCellularNetwork(CellularNetwork network); - virtual void DisconnectFromWirelessNetwork(const WirelessNetwork& network); - virtual void SaveCellularNetwork(const CellularNetwork& network); - virtual void SaveWifiNetwork(const WifiNetwork& network); - virtual void ForgetWirelessNetwork(const WirelessNetwork& network); - - virtual bool ethernet_available() const { - return available_devices_ & (1 << TYPE_ETHERNET); - } - virtual bool wifi_available() const { - return available_devices_ & (1 << TYPE_WIFI); - } - virtual bool cellular_available() const { - return available_devices_ & (1 << TYPE_CELLULAR); - } - - virtual bool ethernet_enabled() const { - return enabled_devices_ & (1 << TYPE_ETHERNET); - } - virtual bool wifi_enabled() const { - return enabled_devices_ & (1 << TYPE_WIFI); - } - virtual bool cellular_enabled() const { - return enabled_devices_ & (1 << TYPE_CELLULAR); - } - - virtual bool offline_mode() const { return offline_mode_; } - - virtual void EnableEthernetNetworkDevice(bool enable); - virtual void EnableWifiNetworkDevice(bool enable); - virtual void EnableCellularNetworkDevice(bool enable); - virtual void EnableOfflineMode(bool enable); - virtual NetworkIPConfigVector GetIPConfigs(const std::string& device_path); - virtual std::string GetHtmlInfo(int refresh); - - virtual void UpdateSystemInfo(); - - private: - - // This method is called when there's a change in network status. - // This method is called on a background thread. - static void NetworkStatusChangedHandler(void* object); - - // This parses SystemInfo into: - // - an EthernetNetwork - // - a WifiNetworkVector of wifi networks - // - a CellularNetworkVector of cellular networks. - // - a WifiNetworkVector of remembered wifi networks - // - a CellularNetworkVector of remembered cellular networks. - static void ParseSystem(SystemInfo* system, - EthernetNetwork* ethernet, - WifiNetworkVector* wifi_networks, - CellularNetworkVector* ceullular_networks, - WifiNetworkVector* remembered_wifi_networks, - CellularNetworkVector* remembered_ceullular_networks); - - // This methods loads the initial list of networks on startup and starts the - // monitoring of network changes. - void Init(); - - // Returns the preferred wifi network. - WifiNetwork* GetPreferredNetwork(); - - // Gets the WifiNetwork with the given name. Returns NULL if not found. - // Only used by GetPreferredNetwork() to lookup "Google" and "GoogleA" (hack) - WifiNetwork* GetWifiNetworkByName(const std::string& name); - - // Gets the WirelessNetwork (WifiNetwork or CellularNetwork) by path - template<typename T> - T* GetWirelessNetworkByPath(std::vector<T>& networks, - const std::string& path); - template<typename T> - const T* GetWirelessNetworkByPath(const std::vector<T>& networks, - const std::string& path) const; - - // Enables/disables the specified network device. - void EnableNetworkDeviceType(ConnectionType device, bool enable); - - // Update the cached network status. - // This will notify all the Observers. - void UpdateNetworkStatus(); - - // Checks network traffic to see if there is any uploading. - // If there is download traffic, then true is passed in for download. - // If there is network traffic then start timer that invokes - // NetworkTrafficTimerFired. - void CheckNetworkTraffic(bool download); - - // Called when the timer fires and we need to send out NetworkTraffic - // notifications. - void NetworkTrafficTimerFired(); - - // This is a helper method to notify the observers on the UI thread. - void NotifyNetworkTraffic(int traffic_type); - - // This will notify all obeservers on the UI thread. - void NotifyObservers(); - - ObserverList<Observer> observers_; - - // The amount of time to wait between each NetworkTraffic notifications. - static const int kNetworkTrafficeTimerSecs; - - // Timer for sending NetworkTraffic notification every - // kNetworkTrafficeTimerSecs seconds. - base::OneShotTimer<NetworkLibraryImpl> timer_; - - // The current traffic type that will be sent out for the next NetworkTraffic - // notification. This is a bitfield of TrafficTypeMasks. - int traffic_type_; - - // The network status connection for monitoring network status changes. - MonitorNetworkConnection network_status_connection_; - - // The ethernet network. - EthernetNetwork ethernet_; - - // The list of available wifi networks. - WifiNetworkVector wifi_networks_; - - // The current connected (or connecting) wifi network. - WifiNetwork wifi_; - - // The remembered wifi networks. - WifiNetworkVector remembered_wifi_networks_; - - // The list of available cellular networks. - CellularNetworkVector cellular_networks_; - - // The current connected (or connecting) cellular network. - CellularNetwork cellular_; - - // The remembered cellular networks. - CellularNetworkVector remembered_cellular_networks_; - - // The current available network devices. Bitwise flag of ConnectionTypes. - int available_devices_; - - // The current enabled network devices. Bitwise flag of ConnectionTypes. - int enabled_devices_; - - // The current connected network devices. Bitwise flag of ConnectionTypes. - int connected_devices_; - - bool offline_mode_; - DISALLOW_COPY_AND_ASSIGN(NetworkLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static NetworkLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/power_library.cc b/chrome/browser/chromeos/cros/power_library.cc index 42978aa..220cd4e 100644 --- a/chrome/browser/chromeos/cros/power_library.cc +++ b/chrome/browser/chromeos/cros/power_library.cc @@ -9,87 +9,129 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::PowerLibraryImpl); - namespace chromeos { -PowerLibraryImpl::PowerLibraryImpl() - : power_status_connection_(NULL), - status_(chromeos::PowerStatus()) { - if (CrosLibrary::Get()->EnsureLoaded()) { - Init(); +class PowerLibraryImpl : public PowerLibrary { + public: + PowerLibraryImpl() + : power_status_connection_(NULL), + status_(chromeos::PowerStatus()) { + if (CrosLibrary::Get()->EnsureLoaded()) { + Init(); + } } -} -PowerLibraryImpl::~PowerLibraryImpl() { - if (power_status_connection_) { - chromeos::DisconnectPowerStatus(power_status_connection_); + ~PowerLibraryImpl() { + if (power_status_connection_) { + chromeos::DisconnectPowerStatus(power_status_connection_); + } } -} -void PowerLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void PowerLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -bool PowerLibraryImpl::line_power_on() const { - return status_.line_power_on; -} + bool line_power_on() const { + return status_.line_power_on; + } -bool PowerLibraryImpl::battery_is_present() const { - return status_.battery_is_present; -} + bool battery_is_present() const { + return status_.battery_is_present; + } -bool PowerLibraryImpl::battery_fully_charged() const { - return status_.battery_state == chromeos::BATTERY_STATE_FULLY_CHARGED; -} + bool battery_fully_charged() const { + return status_.battery_state == chromeos::BATTERY_STATE_FULLY_CHARGED; + } -double PowerLibraryImpl::battery_percentage() const { - return status_.battery_percentage; -} + double battery_percentage() const { + return status_.battery_percentage; + } -base::TimeDelta PowerLibraryImpl::battery_time_to_empty() const { - return base::TimeDelta::FromSeconds(status_.battery_time_to_empty); -} + base::TimeDelta battery_time_to_empty() const { + return base::TimeDelta::FromSeconds(status_.battery_time_to_empty); + } -base::TimeDelta PowerLibraryImpl::battery_time_to_full() const { - return base::TimeDelta::FromSeconds(status_.battery_time_to_full); -} + base::TimeDelta battery_time_to_full() const { + return base::TimeDelta::FromSeconds(status_.battery_time_to_full); + } -// static -void PowerLibraryImpl::PowerStatusChangedHandler(void* object, - const chromeos::PowerStatus& status) { - PowerLibraryImpl* power = static_cast<PowerLibraryImpl*>(object); - power->UpdatePowerStatus(status); -} + private: + static void PowerStatusChangedHandler(void* object, + const chromeos::PowerStatus& status) { + PowerLibraryImpl* power = static_cast<PowerLibraryImpl*>(object); + power->UpdatePowerStatus(status); + } -void PowerLibraryImpl::Init() { - power_status_connection_ = chromeos::MonitorPowerStatus( - &PowerStatusChangedHandler, this); -} + void Init() { + power_status_connection_ = chromeos::MonitorPowerStatus( + &PowerStatusChangedHandler, this); + } -void PowerLibraryImpl::UpdatePowerStatus(const chromeos::PowerStatus& status) { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &PowerLibraryImpl::UpdatePowerStatus, status)); - return; + void UpdatePowerStatus(const chromeos::PowerStatus& status) { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, &PowerLibraryImpl::UpdatePowerStatus, status)); + return; + } + + DLOG(INFO) << "Power" << + " lpo=" << status.line_power_on << + " sta=" << status.battery_state << + " per=" << status.battery_percentage << + " tte=" << status.battery_time_to_empty << + " ttf=" << status.battery_time_to_full; + status_ = status; + FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(this)); } - DLOG(INFO) << "Power" << - " lpo=" << status.line_power_on << - " sta=" << status.battery_state << - " per=" << status.battery_percentage << - " tte=" << status.battery_time_to_empty << - " ttf=" << status.battery_time_to_full; - status_ = status; - FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(this)); + ObserverList<Observer> observers_; + + // A reference to the battery power api, to allow callbacks when the battery + // status changes. + chromeos::PowerStatusConnection power_status_connection_; + + // The latest power status. + chromeos::PowerStatus status_; + + DISALLOW_COPY_AND_ASSIGN(PowerLibraryImpl); +}; + +class PowerLibraryStubImpl : public PowerLibrary { + public: + PowerLibraryStubImpl() {} + ~PowerLibraryStubImpl() {} + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + bool line_power_on() const { return false; } + bool battery_is_present() const { return false; } + bool battery_fully_charged() const { return false; } + double battery_percentage() const { return false; } + base::TimeDelta battery_time_to_empty() const { + return base::TimeDelta::FromSeconds(0); + } + base::TimeDelta battery_time_to_full() const { + return base::TimeDelta::FromSeconds(0); + } +}; + +// static +PowerLibrary* PowerLibrary::GetImpl(bool stub) { + if (stub) + return new PowerLibraryStubImpl(); + else + return new PowerLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::PowerLibraryImpl); + diff --git a/chrome/browser/chromeos/cros/power_library.h b/chrome/browser/chromeos/cros/power_library.h index 76ab81a..10e6df4 100644 --- a/chrome/browser/chromeos/cros/power_library.h +++ b/chrome/browser/chromeos/cros/power_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_POWER_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_POWER_LIBRARY_H_ +#pragma once #include "base/observer_list.h" #include "base/singleton.h" @@ -14,7 +15,7 @@ namespace chromeos { // This interface defines interaction with the ChromeOS power library APIs. // Classes can add themselves as observers. Users can get an instance of this -// library class like this: PowerLibrary::Get() +// library class like this: chromeos::CrosLibrary::Get()->GetPowerLibrary() class PowerLibrary { public: class Observer { @@ -41,63 +42,10 @@ class PowerLibrary { // The amount of time until battery is full. virtual base::TimeDelta battery_time_to_full() const = 0; -}; - - -// This class handles the interaction with the ChromeOS power library APIs. -// Classes can add themselves as observers. Users can get an instance of this -// library class like this: PowerLibrary::Get() -class PowerLibraryImpl : public PowerLibrary { - public: - PowerLibraryImpl(); - virtual ~PowerLibraryImpl(); - - // PowerLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - - // Whether or not the line power is connected. - virtual bool line_power_on() const; - - // Whether or not the battery is fully charged.. - virtual bool battery_fully_charged() const; - - // The percentage (0-100) of remaining battery. - virtual double battery_percentage() const; - - // Whether there is a battery present. - virtual bool battery_is_present() const; - - // The amount of time until battery is empty. - virtual base::TimeDelta battery_time_to_empty() const; - - // The amount of time until battery is full. - virtual base::TimeDelta battery_time_to_full() const; - - private: - - // This method is called when there's a change in power status. - // This method is called on a background thread. - static void PowerStatusChangedHandler(void* object, - const chromeos::PowerStatus& status); - - // This methods starts the monitoring of power changes. - void Init(); - - // Called by the handler to update the power status. - // This will notify all the Observers. - void UpdatePowerStatus(const chromeos::PowerStatus& status); - - ObserverList<Observer> observers_; - - // A reference to the battery power api, to allow callbacks when the battery - // status changes. - chromeos::PowerStatusConnection power_status_connection_; - - // The latest power status. - chromeos::PowerStatus status_; - DISALLOW_COPY_AND_ASSIGN(PowerLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static PowerLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/screen_lock_library.cc b/chrome/browser/chromeos/cros/screen_lock_library.cc index 731ec43..0115683 100644 --- a/chrome/browser/chromeos/cros/screen_lock_library.cc +++ b/chrome/browser/chromeos/cros/screen_lock_library.cc @@ -9,135 +9,170 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::ScreenLockLibraryImpl); - namespace chromeos { -ScreenLockLibraryImpl::ScreenLockLibraryImpl() { - if (CrosLibrary::Get()->EnsureLoaded()) { - Init(); +// This class handles the interaction with the ChromeOS screen lock APIs. +class ScreenLockLibraryImpl : public ScreenLockLibrary { + public: + ScreenLockLibraryImpl() { + if (CrosLibrary::Get()->EnsureLoaded()) { + Init(); + } } -} -ScreenLockLibraryImpl::~ScreenLockLibraryImpl() { - if (screen_lock_connection_) { - chromeos::DisconnectScreenLock(screen_lock_connection_); + ~ScreenLockLibraryImpl() { + if (screen_lock_connection_) { + chromeos::DisconnectScreenLock(screen_lock_connection_); + } } -} -void ScreenLockLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void ScreenLockLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -void ScreenLockLibraryImpl::NotifyScreenLockRequested() { - // Make sure we run on IO thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { - ChromeThread::PostTask( - ChromeThread::IO, FROM_HERE, - NewRunnableMethod(this, - &ScreenLockLibraryImpl::NotifyScreenLockRequested)); - return; + void NotifyScreenLockRequested() { + // Make sure we run on IO thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &ScreenLockLibraryImpl::NotifyScreenLockRequested)); + return; + } + chromeos::NotifyScreenLockRequested(); } - chromeos::NotifyScreenLockRequested(); -} -void ScreenLockLibraryImpl::NotifyScreenLockCompleted() { - // Make sure we run on IO thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { - ChromeThread::PostTask( - ChromeThread::IO, FROM_HERE, - NewRunnableMethod(this, - &ScreenLockLibraryImpl::NotifyScreenLockCompleted)); - return; + void NotifyScreenLockCompleted() { + // Make sure we run on IO thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &ScreenLockLibraryImpl::NotifyScreenLockCompleted)); + return; + } + chromeos::NotifyScreenLockCompleted(); } - chromeos::NotifyScreenLockCompleted(); -} -void ScreenLockLibraryImpl::NotifyScreenUnlockRequested() { - // Make sure we run on IO thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { - ChromeThread::PostTask( - ChromeThread::IO, FROM_HERE, - NewRunnableMethod(this, - &ScreenLockLibraryImpl::NotifyScreenUnlockRequested)); - return; + void NotifyScreenUnlockRequested() { + // Make sure we run on IO thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &ScreenLockLibraryImpl::NotifyScreenUnlockRequested)); + return; + } + chromeos::NotifyScreenUnlockRequested(); } - chromeos::NotifyScreenUnlockRequested(); -} -void ScreenLockLibraryImpl::NotifyScreenUnlockCompleted() { - // Make sure we run on IO thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { - ChromeThread::PostTask( - ChromeThread::IO, FROM_HERE, - NewRunnableMethod(this, - &ScreenLockLibraryImpl::NotifyScreenUnlockCompleted)); - return; + void NotifyScreenUnlockCompleted() { + // Make sure we run on IO thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &ScreenLockLibraryImpl::NotifyScreenUnlockCompleted)); + return; + } + chromeos::NotifyScreenUnlockCompleted(); } - chromeos::NotifyScreenUnlockCompleted(); -} -void ScreenLockLibraryImpl::Init() { - screen_lock_connection_ = chromeos::MonitorScreenLock( - &ScreenLockedHandler, this); -} + private: + void Init() { + screen_lock_connection_ = chromeos::MonitorScreenLock( + &ScreenLockedHandler, this); + } -void ScreenLockLibraryImpl::LockScreen() { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &ScreenLockLibraryImpl::LockScreen)); - return; + void LockScreen() { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &ScreenLockLibraryImpl::LockScreen)); + return; + } + FOR_EACH_OBSERVER(Observer, observers_, LockScreen(this)); } - FOR_EACH_OBSERVER(Observer, observers_, LockScreen(this)); -} -void ScreenLockLibraryImpl::UnlockScreen() { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &ScreenLockLibraryImpl::UnlockScreen)); - return; + void UnlockScreen() { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &ScreenLockLibraryImpl::UnlockScreen)); + return; + } + FOR_EACH_OBSERVER(Observer, observers_, UnlockScreen(this)); } - FOR_EACH_OBSERVER(Observer, observers_, UnlockScreen(this)); -} -void ScreenLockLibraryImpl::UnlockScreenFailed() { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &ScreenLockLibraryImpl::UnlockScreenFailed)); - return; + void UnlockScreenFailed() { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &ScreenLockLibraryImpl::UnlockScreenFailed)); + return; + } + FOR_EACH_OBSERVER(Observer, observers_, UnlockScreenFailed(this)); } - FOR_EACH_OBSERVER(Observer, observers_, UnlockScreenFailed(this)); -} -// static -void ScreenLockLibraryImpl::ScreenLockedHandler(void* object, - ScreenLockEvent event) { - ScreenLockLibraryImpl* self = static_cast<ScreenLockLibraryImpl*>(object); - switch (event) { - case chromeos::LockScreen: - self->LockScreen(); - break; - case chromeos::UnlockScreen: - self->UnlockScreen(); - break; - case chromeos::UnlockScreenFailed: - self->UnlockScreenFailed(); - break; - default: - NOTREACHED(); + static void ScreenLockedHandler(void* object, ScreenLockEvent event) { + ScreenLockLibraryImpl* self = static_cast<ScreenLockLibraryImpl*>(object); + switch (event) { + case chromeos::LockScreen: + self->LockScreen(); + break; + case chromeos::UnlockScreen: + self->UnlockScreen(); + break; + case chromeos::UnlockScreenFailed: + self->UnlockScreenFailed(); + break; + default: + NOTREACHED(); + } } + + ObserverList<Observer> observers_; + + // A reference to the screen lock api + chromeos::ScreenLockConnection screen_lock_connection_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockLibraryImpl); +}; + +class ScreenLockLibraryStubImpl : public ScreenLockLibrary { + public: + ScreenLockLibraryStubImpl() {} + ~ScreenLockLibraryStubImpl() {} + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + void NotifyScreenLockRequested() {} + void NotifyScreenLockCompleted() {} + void NotifyScreenUnlockRequested() {} + void NotifyScreenUnlockCompleted() {} +}; + +// static +ScreenLockLibrary* ScreenLockLibrary::GetImpl(bool stub) { + if (stub) + return new ScreenLockLibraryStubImpl(); + else + return new ScreenLockLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::ScreenLockLibraryImpl); + diff --git a/chrome/browser/chromeos/cros/screen_lock_library.h b/chrome/browser/chromeos/cros/screen_lock_library.h index 829d156..251c6f4 100644 --- a/chrome/browser/chromeos/cros/screen_lock_library.h +++ b/chrome/browser/chromeos/cros/screen_lock_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SCREEN_LOCK_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SCREEN_LOCK_LIBRARY_H_ +#pragma once #include "base/observer_list.h" #include "cros/chromeos_screen_lock.h" @@ -34,48 +35,10 @@ class ScreenLockLibrary { virtual void NotifyScreenUnlockRequested() = 0; // Notifies PowerManager that screen is unlocked. virtual void NotifyScreenUnlockCompleted() = 0; -}; - -// This class handles the interaction with the ChromeOS screen lock APIs. -class ScreenLockLibraryImpl : public ScreenLockLibrary { - public: - ScreenLockLibraryImpl(); - virtual ~ScreenLockLibraryImpl(); - - // ScreenLockLibrary implementations: - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - virtual void NotifyScreenLockRequested(); - virtual void NotifyScreenLockCompleted(); - virtual void NotifyScreenUnlockRequested(); - virtual void NotifyScreenUnlockCompleted(); - - private: - // This method is called when PowerManager requests to lock the screen. - // This method is called on a background thread. - static void ScreenLockedHandler(void* object, ScreenLockEvent event); - - // This methods starts the monitoring of screen lock request. - void Init(); - - // Called by the handler to notify the screen lock request from - // SessionManager. - void LockScreen(); - - // Called by the handler to notify the screen unlock request from - // SessionManager. - void UnlockScreen(); - - // Called by the handler to notify the screen unlock request has been - // failed. - void UnlockScreenFailed(); - - ObserverList<Observer> observers_; - - // A reference to the screen lock api - chromeos::ScreenLockConnection screen_lock_connection_; - DISALLOW_COPY_AND_ASSIGN(ScreenLockLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static ScreenLockLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/speech_synthesis_library.cc b/chrome/browser/chromeos/cros/speech_synthesis_library.cc index 10194ee..0325c1f 100644 --- a/chrome/browser/chromeos/cros/speech_synthesis_library.cc +++ b/chrome/browser/chromeos/cros/speech_synthesis_library.cc @@ -11,24 +11,55 @@ namespace chromeos { -bool SpeechSynthesisLibraryImpl::Speak(const char* text) { - return chromeos::Speak(text); -} +class SpeechSynthesisLibraryImpl : public SpeechSynthesisLibrary { + public: + SpeechSynthesisLibraryImpl() {} + virtual ~SpeechSynthesisLibraryImpl() {} -bool SpeechSynthesisLibraryImpl::SetSpeakProperties(const char* props) { - return chromeos::SetSpeakProperties(props); -} + bool Speak(const char* text) { + return chromeos::Speak(text); + } -bool SpeechSynthesisLibraryImpl::StopSpeaking() { - return chromeos::StopSpeaking(); -} + bool SetSpeakProperties(const char* props) { + return chromeos::SetSpeakProperties(props); + } -bool SpeechSynthesisLibraryImpl::IsSpeaking() { - return chromeos::IsSpeaking(); -} + bool StopSpeaking() { + return chromeos::StopSpeaking(); + } + + bool IsSpeaking() { + return chromeos::IsSpeaking(); + } + + void InitTts(InitStatusCallback callback) { + chromeos::InitTts(callback); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisLibraryImpl); +}; + +class SpeechSynthesisLibraryStubImpl : public SpeechSynthesisLibrary { + public: + SpeechSynthesisLibraryStubImpl() {} + virtual ~SpeechSynthesisLibraryStubImpl() {} + bool Speak(const char* text) { return true; } + bool SetSpeakProperties(const char* props) { return true; } + bool StopSpeaking() { return true; } + bool IsSpeaking() { return false; } + void InitTts(InitStatusCallback callback) {} + + private: + DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisLibraryStubImpl); +}; -void SpeechSynthesisLibraryImpl::InitTts(InitStatusCallback callback) { - chromeos::InitTts(callback); +// static +SpeechSynthesisLibrary* SpeechSynthesisLibrary::GetImpl(bool stub) { + if (stub) + return new SpeechSynthesisLibraryStubImpl(); + else + return new SpeechSynthesisLibraryImpl(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/speech_synthesis_library.h b/chrome/browser/chromeos/cros/speech_synthesis_library.h index 10f2c11..5ba97d1 100644 --- a/chrome/browser/chromeos/cros/speech_synthesis_library.h +++ b/chrome/browser/chromeos/cros/speech_synthesis_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SPEECH_SYNTHESIS_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SPEECH_SYNTHESIS_LIBRARY_H_ +#pragma once #include "base/singleton.h" @@ -26,23 +27,10 @@ class SpeechSynthesisLibrary { // Starts the speech synthesis service and indicates through a callback if // it started successfully. virtual void InitTts(InitStatusCallback) = 0; -}; -// This class handles the interaction with the ChromeOS login library APIs. -class SpeechSynthesisLibraryImpl : public SpeechSynthesisLibrary { - public: - SpeechSynthesisLibraryImpl() {} - virtual ~SpeechSynthesisLibraryImpl() {} - - // SpeechSynthesisLibrary overrides. - virtual bool Speak(const char* text); - virtual bool SetSpeakProperties(const char* props); - virtual bool StopSpeaking(); - virtual bool IsSpeaking(); - virtual void InitTts(InitStatusCallback); - - private: - DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static SpeechSynthesisLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/synaptics_library.cc b/chrome/browser/chromeos/cros/synaptics_library.cc deleted file mode 100644 index 462be76..0000000 --- a/chrome/browser/chromeos/cros/synaptics_library.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2009 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/cros/synaptics_library.h" - -#include "base/message_loop.h" -#include "chrome/browser/chrome_thread.h" -#include "chrome/browser/chromeos/cros/cros_library.h" - -namespace chromeos { - -void SynapticsLibraryImpl::SetBoolParameter(SynapticsParameter param, - bool value) { - SetParameter(param, value ? 1 : 0); -} - -void SynapticsLibraryImpl::SetRangeParameter(SynapticsParameter param, - int value) { - if (value < 1) - value = 1; - if (value > 10) - value = 10; - SetParameter(param, value); -} - -void SynapticsLibraryImpl::SetParameter(SynapticsParameter param, int value) { - if (CrosLibrary::Get()->EnsureLoaded()) { - // This calls SetSynapticsParameter in the cros library which is - // potentially time consuming. So we run this on the FILE thread. - ChromeThread::PostTask( - ChromeThread::FILE, FROM_HERE, - NewRunnableFunction(&SetSynapticsParameter, param, value)); - } -} - -} // namespace chromeos diff --git a/chrome/browser/chromeos/cros/synaptics_library.h b/chrome/browser/chromeos/cros/synaptics_library.h deleted file mode 100644 index 3dfd69f..0000000 --- a/chrome/browser/chromeos/cros/synaptics_library.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2009 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_CROS_SYNAPTICS_LIBRARY_H_ -#define CHROME_BROWSER_CHROMEOS_CROS_SYNAPTICS_LIBRARY_H_ - -#include "base/singleton.h" -#include "cros/chromeos_synaptics.h" - -namespace chromeos { - -// This interface defines interaction with the ChromeOS synaptics library APIs. -// Users can get an instance of this library class like this: -// SynapticsLibrary::Get() -// For a list of SynapticsPrameters, see chromeos_synaptics.h -// in third_party/cros or /usr/include/cros -class SynapticsLibrary { - public: - virtual ~SynapticsLibrary() {} - // Sets a boolean parameter. The actual call will be run on the FILE thread. - virtual void SetBoolParameter(SynapticsParameter param, bool value) = 0; - - // Sets a range parameter. The actual call will be run on the FILE thread. - // Value should be between 1 and 10 inclusive. - virtual void SetRangeParameter(SynapticsParameter param, int value) = 0; -}; - - -// This class handles the interaction with the ChromeOS synaptics library APIs. -// Users can get an instance of this library class like this: -// SynapticsLibrary::Get() -// For a list of SynapticsPrameters, see chromeos_synaptics.h -// in third_party/cros or /usr/include/cros -class SynapticsLibraryImpl : public SynapticsLibrary { - public: - SynapticsLibraryImpl() {} - virtual ~SynapticsLibraryImpl() {} - - // SynapticsLibrary overrides. - virtual void SetBoolParameter(SynapticsParameter param, bool value); - virtual void SetRangeParameter(SynapticsParameter param, int value); - - private: - - // This helper methods calls into the libcros library to set the parameter. - // This call is run on the FILE thread. - void SetParameter(SynapticsParameter param, int value); - - DISALLOW_COPY_AND_ASSIGN(SynapticsLibraryImpl); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_CROS_SYNAPTICS_LIBRARY_H_ diff --git a/chrome/browser/chromeos/cros/syslogs_library.cc b/chrome/browser/chromeos/cros/syslogs_library.cc index 365e9fe..9fed176 100644 --- a/chrome/browser/chromeos/cros/syslogs_library.cc +++ b/chrome/browser/chromeos/cros/syslogs_library.cc @@ -10,11 +10,74 @@ namespace chromeos { -LogDictionaryType* SyslogsLibraryImpl::GetSyslogs(FilePath* tmpfilename) { - if (CrosLibrary::Get()->EnsureLoaded()) { - return chromeos::GetSystemLogs(tmpfilename); +class SyslogsLibraryImpl : public SyslogsLibrary { + public: + SyslogsLibraryImpl() {} + virtual ~SyslogsLibraryImpl() {} + + virtual Handle RequestSyslogs(FilePath* tmpfilename, + CancelableRequestConsumerBase* consumer, + ReadCompleteCallback* callback) { + // Register the callback request. + scoped_refptr<CancelableRequest<ReadCompleteCallback> > request( + new CancelableRequest<ReadCompleteCallback>(callback)); + AddRequest(request, consumer); + + // Schedule a task on the FILE thread which will then trigger a request + // callback on the calling thread (e.g. UI) when complete. + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod( + this, &SyslogsLibraryImpl::ReadSyslogs, request, tmpfilename)); + + return request->handle(); + } + + private: + // Called from FILE thread. + void ReadSyslogs( + scoped_refptr<CancelableRequest<ReadCompleteCallback> > request, + FilePath* tmpfilename) { + if (request->canceled()) + return; + + LogDictionaryType* logs = NULL; + if (CrosLibrary::Get()->EnsureLoaded()) { + logs = chromeos::GetSystemLogs(tmpfilename); + } + + // Will call the callback on the calling thread. + request->ForwardResult(Tuple1<LogDictionaryType*>(logs)); } - return NULL; + + DISALLOW_COPY_AND_ASSIGN(SyslogsLibraryImpl); +}; + +class SyslogsLibraryStubImpl : public SyslogsLibrary { + public: + SyslogsLibraryStubImpl() {} + virtual ~SyslogsLibraryStubImpl() {} + + virtual Handle RequestSyslogs(FilePath* tmpfilename, + CancelableRequestConsumerBase* consumer, + ReadCompleteCallback* callback) { + if (callback) + callback->Run(Tuple1<LogDictionaryType*>(NULL)); + + return 0; + } +}; + +// static +SyslogsLibrary* SyslogsLibrary::GetImpl(bool stub) { + if (stub) + return new SyslogsLibraryStubImpl(); + else + return new SyslogsLibraryImpl(); } } // namespace chromeos + +// Allows InvokeLater without adding refcounting. SyslogsLibraryImpl is a +// Singleton and won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::SyslogsLibraryImpl); diff --git a/chrome/browser/chromeos/cros/syslogs_library.h b/chrome/browser/chromeos/cros/syslogs_library.h index e29824c..56a07f7 100644 --- a/chrome/browser/chromeos/cros/syslogs_library.h +++ b/chrome/browser/chromeos/cros/syslogs_library.h @@ -4,33 +4,36 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SYSLOGS_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SYSLOGS_LIBRARY_H_ +#pragma once #include "base/singleton.h" +#include "chrome/browser/cancelable_request.h" #include "cros/chromeos_syslogs.h" +class CancelableRequestConsumerBase; + namespace chromeos { // This interface defines interaction with the ChromeOS syslogs APIs. -class SyslogsLibrary { +class SyslogsLibrary : public CancelableRequestProvider { public: + typedef Callback1<LogDictionaryType*>::Type ReadCompleteCallback; + SyslogsLibrary() {} virtual ~SyslogsLibrary() {} - // System logs gathered for userfeedback - virtual LogDictionaryType* GetSyslogs(FilePath* tmpfilename) = 0; -}; - - -// This class handles the interaction with the ChromeOS syslogs APIs. -class SyslogsLibraryImpl : public SyslogsLibrary { - public: - SyslogsLibraryImpl() {} - virtual ~SyslogsLibraryImpl() {} - - virtual LogDictionaryType* GetSyslogs(FilePath* tmpfilename); - - private: - DISALLOW_COPY_AND_ASSIGN(SyslogsLibraryImpl); + // Request system logs. Read happens on the FILE thread and callback is + // called on the thread this is called from (via ForwardResult). + // Logs are owned by callback function (use delete when done with them). + // Returns the request handle. Call CancelRequest(Handle) to cancel + // the request before the callback gets called. + virtual Handle RequestSyslogs(FilePath* tmpfilename, + CancelableRequestConsumerBase* consumer, + ReadCompleteCallback* callback) = 0; + + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static SyslogsLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/system_library.cc b/chrome/browser/chromeos/cros/system_library.cc index 7f528d1..b14e38e 100644 --- a/chrome/browser/chromeos/cros/system_library.cc +++ b/chrome/browser/chromeos/cros/system_library.cc @@ -4,52 +4,129 @@ #include "chrome/browser/chromeos/cros/system_library.h" +#include <map> + #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" namespace chromeos { -SystemLibraryImpl::SystemLibraryImpl() { - std::string id = "US/Pacific"; - if (CrosLibrary::Get()->EnsureLoaded()) { - std::string timezone_id = chromeos::GetTimezoneID(); - if (timezone_id.empty()) { - LOG(ERROR) << "Got an empty string for timezone, default to " << id; - } else { - id = timezone_id; +class SystemLibraryImpl : public SystemLibrary { + public: + typedef std::map<std::string, std::string> StringMap; + + SystemLibraryImpl() { + // Get Statistics + UpdateMachineStatistics(); + // Get Timezone + std::string id = "US/Pacific"; + if (CrosLibrary::Get()->EnsureLoaded()) { + std::string timezone_id = chromeos::GetTimezoneID(); + if (timezone_id.empty()) { + LOG(ERROR) << "Got an empty string for timezone, default to " << id; + } else { + id = timezone_id; + } } + icu::TimeZone* timezone = + icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)); + timezone_.reset(timezone); + icu::TimeZone::setDefault(*timezone); + LOG(INFO) << "Timezone is " << id; } - icu::TimeZone* timezone = - icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)); - timezone_.reset(timezone); - icu::TimeZone::setDefault(*timezone); - LOG(INFO) << "Timezone is " << id; -} -void SystemLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void SystemLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -const icu::TimeZone& SystemLibraryImpl::GetTimezone() { - return *timezone_.get(); -} + const icu::TimeZone& GetTimezone() { + return *timezone_.get(); + } + + void SetTimezone(const icu::TimeZone* timezone) { + timezone_.reset(timezone->clone()); + if (CrosLibrary::Get()->EnsureLoaded()) { + icu::UnicodeString unicode; + timezone->getID(unicode); + std::string id; + UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id); + LOG(INFO) << "Setting timezone to " << id; + chromeos::SetTimezoneID(id); + } + icu::TimeZone::setDefault(*timezone); + FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*timezone)); + } + + bool GetMachineStatistic(const std::string& name, std::string* result) { + StringMap::iterator iter = machine_info_.find(name); + if (iter != machine_info_.end()) { + *result = iter->second; + return true; + } + return false; + } + + private: + void UpdateMachineStatistics() { + if (CrosLibrary::Get()->EnsureLoaded()) { + chromeos::MachineInfo* machine_info = chromeos::GetMachineInfo(); + if (!machine_info) { + LOG(ERROR) << "Error calling chromeos::GetMachineInfo()."; + return; + } + // Get Name Value pairs. + for (int i = 0; i<machine_info->name_value_size; ++i) { + const chromeos::MachineInfo::NVPair& nv = machine_info->name_values[i]; + machine_info_[nv.name] = nv.value; + } + chromeos::FreeMachineInfo(machine_info); + } + } + + scoped_ptr<icu::TimeZone> timezone_; + ObserverList<Observer> observers_; + StringMap machine_info_; + + DISALLOW_COPY_AND_ASSIGN(SystemLibraryImpl); +}; + +class SystemLibraryStubImpl : public SystemLibrary { + public: + SystemLibraryStubImpl() { + std::string id = "US/Pacific"; + icu::TimeZone* timezone = + icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)); + timezone_.reset(timezone); + } + ~SystemLibraryStubImpl() {} + + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + const icu::TimeZone& GetTimezone() { + return *timezone_.get(); + } + void SetTimezone(const icu::TimeZone* timezone) {} + + bool GetMachineStatistic(const std::string& name, std::string* result) { + *result = "Stub Statistic:" + name; + return true; + } + + private: + scoped_ptr<icu::TimeZone> timezone_; + DISALLOW_COPY_AND_ASSIGN(SystemLibraryStubImpl); +}; -void SystemLibraryImpl::SetTimezone(const icu::TimeZone* timezone) { - timezone_.reset(timezone->clone()); - if (CrosLibrary::Get()->EnsureLoaded()) { - icu::UnicodeString unicode; - timezone->getID(unicode); - std::string id; - UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id); - LOG(INFO) << "Setting timezone to " << id; - chromeos::SetTimezoneID(id); - } - icu::TimeZone::setDefault(*timezone); - FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*timezone)); +// static +SystemLibrary* SystemLibrary::GetImpl(bool stub) { + if (stub) + return new SystemLibraryStubImpl(); + else + return new SystemLibraryImpl(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/system_library.h b/chrome/browser/chromeos/cros/system_library.h index b33eb3d..9cf6640 100644 --- a/chrome/browser/chromeos/cros/system_library.h +++ b/chrome/browser/chromeos/cros/system_library.h @@ -4,9 +4,9 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SYSTEM_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SYSTEM_LIBRARY_H_ +#pragma once #include "base/observer_list.h" -#include "base/scoped_ptr.h" #include "base/singleton.h" #include "cros/chromeos_system.h" #include "unicode/timezone.h" @@ -32,26 +32,15 @@ class SystemLibrary { // Sets the current timezone. |timezone| must be non-null. virtual void SetTimezone(const icu::TimeZone* timezone) = 0; -}; - -// This class handles the interaction with the ChromeOS syslogs APIs. -class SystemLibraryImpl : public SystemLibrary { - public: - SystemLibraryImpl(); - virtual ~SystemLibraryImpl() {} - - // NetworkLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - - virtual const icu::TimeZone& GetTimezone(); - virtual void SetTimezone(const icu::TimeZone*); - private: - scoped_ptr<icu::TimeZone> timezone_; - ObserverList<Observer> observers_; + // Retrieve the named machine statistic (e.g. "hardware_class"). + // This does not update the statistcs (i.e. does not call into libcros). + virtual bool GetMachineStatistic(const std::string& name, + std::string* result) = 0; - DISALLOW_COPY_AND_ASSIGN(SystemLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static SystemLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros/update_library.cc b/chrome/browser/chromeos/cros/update_library.cc index 0ea5f2c..26a11ef 100644 --- a/chrome/browser/chromeos/cros/update_library.cc +++ b/chrome/browser/chromeos/cros/update_library.cc @@ -9,60 +9,114 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" -// Allows InvokeLater without adding refcounting. This class is a Singleton and -// won't be deleted until it's last InvokeLater is run. -DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::UpdateLibraryImpl); - namespace chromeos { -UpdateLibraryImpl::UpdateLibraryImpl() +class UpdateLibraryImpl : public UpdateLibrary { + public: + UpdateLibraryImpl() : status_connection_(NULL) { - if (CrosLibrary::Get()->EnsureLoaded()) { - Init(); + if (CrosLibrary::Get()->EnsureLoaded()) { + Init(); + } } -} -UpdateLibraryImpl::~UpdateLibraryImpl() { - if (status_connection_) { - DisconnectUpdateProgress(status_connection_); + ~UpdateLibraryImpl() { + if (status_connection_) { + DisconnectUpdateProgress(status_connection_); + } } -} -void UpdateLibraryImpl::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} + void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } -void UpdateLibraryImpl::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } -const UpdateLibrary::Status& UpdateLibraryImpl::status() const { - return status_; -} + bool CheckForUpdate() { + if (!CrosLibrary::Get()->EnsureLoaded()) + return false; -// static -void UpdateLibraryImpl::ChangedHandler(void* object, - const UpdateProgress& status) { - UpdateLibraryImpl* power = static_cast<UpdateLibraryImpl*>(object); - power->UpdateStatus(Status(status)); -} + return InitiateUpdateCheck(); + } -void UpdateLibraryImpl::Init() { - status_connection_ = MonitorUpdateStatus(&ChangedHandler, this); -} + bool RebootAfterUpdate() { + if (!CrosLibrary::Get()->EnsureLoaded()) + return false; + + return RebootIfUpdated(); + } + + const UpdateLibrary::Status& status() const { + return status_; + } -void UpdateLibraryImpl::UpdateStatus(const Status& status) { - // Make sure we run on UI thread. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &UpdateLibraryImpl::UpdateStatus, status)); - return; + private: + static void ChangedHandler(void* object, + const UpdateProgress& status) { + UpdateLibraryImpl* updater = static_cast<UpdateLibraryImpl*>(object); + updater->UpdateStatus(Status(status)); } - status_ = status; - FOR_EACH_OBSERVER(Observer, observers_, Changed(this)); + void Init() { + status_connection_ = MonitorUpdateStatus(&ChangedHandler, this); + } + + void UpdateStatus(const Status& status) { + // Make sure we run on UI thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &UpdateLibraryImpl::UpdateStatus, status)); + return; + } + + status_ = status; + FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(this)); + } + + ObserverList<Observer> observers_; + + // A reference to the update api, to allow callbacks when the update + // status changes. + UpdateStatusConnection status_connection_; + + // The latest power status. + Status status_; + + DISALLOW_COPY_AND_ASSIGN(UpdateLibraryImpl); +}; + +class UpdateLibraryStubImpl : public UpdateLibrary { + public: + UpdateLibraryStubImpl() {} + ~UpdateLibraryStubImpl() {} + void AddObserver(Observer* observer) {} + void RemoveObserver(Observer* observer) {} + bool CheckForUpdate() { return false; } + bool RebootAfterUpdate() { return false; } + const UpdateLibrary::Status& status() const { + return status_; + } + + private: + Status status_; + + DISALLOW_COPY_AND_ASSIGN(UpdateLibraryStubImpl); +}; + +// static +UpdateLibrary* UpdateLibrary::GetImpl(bool stub) { + if (stub) + return new UpdateLibraryStubImpl(); + else + return new UpdateLibraryImpl(); } } // namespace chromeos +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::UpdateLibraryImpl); + diff --git a/chrome/browser/chromeos/cros/update_library.h b/chrome/browser/chromeos/cros/update_library.h index c57e7bd..7b95815 100644 --- a/chrome/browser/chromeos/cros/update_library.h +++ b/chrome/browser/chromeos/cros/update_library.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_UPDATE_LIBRARY_H_ #define CHROME_BROWSER_CHROMEOS_CROS_UPDATE_LIBRARY_H_ +#pragma once #include <string> @@ -52,50 +53,26 @@ class UpdateLibrary { class Observer { public: virtual ~Observer() { } - virtual void Changed(UpdateLibrary* obj) = 0; + virtual void UpdateStatusChanged(UpdateLibrary* library) = 0; }; +// static UpdateLibrary* GetStubImplementation(); + virtual ~UpdateLibrary() {} virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; - virtual const Status& status() const = 0; -}; - -class UpdateLibraryImpl : public UpdateLibrary { - public: - UpdateLibraryImpl(); - virtual ~UpdateLibraryImpl(); - - // UpdateLibrary overrides. - virtual void AddObserver(Observer* observer); - virtual void RemoveObserver(Observer* observer); - - virtual const Status& status() const; + // Initiates update check and returns true if check was initiated. + virtual bool CheckForUpdate() = 0; - private: + // Reboots if update has been performed. + virtual bool RebootAfterUpdate() = 0; - // This method is called when there's a change in status. - // This method is called on a background thread. - static void ChangedHandler(void* object, const UpdateProgress& status); - - // This methods starts the monitoring of power changes. - void Init(); - - // Called by the handler to update the power status. - // This will notify all the Observers. - void UpdateStatus(const Status& status); - - ObserverList<Observer> observers_; - - // A reference to the update api, to allow callbacks when the update - // status changes. - UpdateStatusConnection status_connection_; - - // The latest power status. - Status status_; + virtual const Status& status() const = 0; - DISALLOW_COPY_AND_ASSIGN(UpdateLibraryImpl); + // Factory function, creates a new instance and returns ownership. + // For normal usage, access the singleton via CrosLibrary::Get(). + static UpdateLibrary* GetImpl(bool stub); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings.cc b/chrome/browser/chromeos/cros_settings.cc index 852ad9d..a0e1297 100644 --- a/chrome/browser/chromeos/cros_settings.cc +++ b/chrome/browser/chromeos/cros_settings.cc @@ -6,38 +6,63 @@ #include "base/singleton.h" #include "base/string_util.h" -#include "chrome/browser/chromeos/mock_cros_settings.h" +#include "base/values.h" +#include "chrome/browser/chromeos/cros_settings_provider.h" +#include "chrome/browser/chromeos/cros_settings_provider_user.h" +#include "chrome/common/notification_service.h" namespace chromeos { CrosSettings* CrosSettings::Get() { // TODO(xiyaun): Use real stuff when underlying libcros is ready. - return Singleton<MockCrosSettings>::get(); + return Singleton<CrosSettings>::get(); } -bool CrosSettings::IsCrosSettings(const std::wstring& path) { - return StartsWith(path, kCrosSettingsPrefix, true); +bool CrosSettings::IsCrosSettings(const std::string& path) { + return StartsWithASCII(path, kCrosSettingsPrefix, true); } -void CrosSettings::SetBoolean(const std::wstring& path, bool in_value) { +void CrosSettings::FireObservers(const char* path) { + DCHECK(CalledOnValidThread()); + std::string path_str(path); + SettingsObserverMap::iterator observer_iterator = + settings_observers_.find(path_str); + if (observer_iterator == settings_observers_.end()) + return; + + NotificationObserverList::Iterator it(*(observer_iterator->second)); + NotificationObserver* observer; + while ((observer = it.GetNext()) != NULL) { + observer->Observe(NotificationType::SYSTEM_SETTING_CHANGED, + Source<CrosSettings>(this), + Details<std::string>(&path_str)); + } +} + +void CrosSettings::SetBoolean(const std::string& path, bool in_value) { + DCHECK(CalledOnValidThread()); Set(path, Value::CreateBooleanValue(in_value)); } -void CrosSettings::SetInteger(const std::wstring& path, int in_value) { +void CrosSettings::SetInteger(const std::string& path, int in_value) { + DCHECK(CalledOnValidThread()); Set(path, Value::CreateIntegerValue(in_value)); } -void CrosSettings::SetReal(const std::wstring& path, double in_value) { +void CrosSettings::SetReal(const std::string& path, double in_value) { + DCHECK(CalledOnValidThread()); Set(path, Value::CreateRealValue(in_value)); } -void CrosSettings::SetString(const std::wstring& path, +void CrosSettings::SetString(const std::string& path, const std::string& in_value) { + DCHECK(CalledOnValidThread()); Set(path, Value::CreateStringValue(in_value)); } -bool CrosSettings::GetBoolean(const std::wstring& path, +bool CrosSettings::GetBoolean(const std::string& path, bool* bool_value) const { + DCHECK(CalledOnValidThread()); Value* value; if (!Get(path, &value)) return false; @@ -45,8 +70,105 @@ bool CrosSettings::GetBoolean(const std::wstring& path, return value->GetAsBoolean(bool_value); } -bool CrosSettings::GetInteger(const std::wstring& path, +bool CrosSettings::AddSettingsProvider(CrosSettingsProvider* provider) { + DCHECK(CalledOnValidThread()); + providers_.push_back(provider); + return true; +} + +bool CrosSettings::RemoveSettingsProvider(CrosSettingsProvider* provider) { + DCHECK(CalledOnValidThread()); + std::vector<CrosSettingsProvider*>::iterator it = + std::find(providers_.begin(), providers_.end(), provider); + if (it != providers_.end()) { + providers_.erase(it); + return true; + } + return false; +} + +void CrosSettings::AddSettingsObserver(const char* path, + NotificationObserver* obs) { + DCHECK(path); + DCHECK(obs); + DCHECK(CalledOnValidThread()); + + if (!GetProvider(std::string(path))) { + NOTREACHED() << "Trying to add an observer for an unregistered setting: " + << path; + return; + } + + // Get the settings observer list associated with the path. + NotificationObserverList* observer_list = NULL; + SettingsObserverMap::iterator observer_iterator = + settings_observers_.find(path); + if (observer_iterator == settings_observers_.end()) { + observer_list = new NotificationObserverList; + settings_observers_[path] = observer_list; + } else { + observer_list = observer_iterator->second; + } + + // Verify that this observer doesn't already exist. + NotificationObserverList::Iterator it(*observer_list); + NotificationObserver* existing_obs; + while ((existing_obs = it.GetNext()) != NULL) { + DCHECK(existing_obs != obs) << path << " observer already registered"; + if (existing_obs == obs) + return; + } + + // Ok, safe to add the pref observer. + observer_list->AddObserver(obs); +} + +void CrosSettings::RemoveSettingsObserver(const char* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + SettingsObserverMap::iterator observer_iterator = + settings_observers_.find(path); + if (observer_iterator == settings_observers_.end()) { + return; + } + + NotificationObserverList* observer_list = observer_iterator->second; + observer_list->RemoveObserver(obs); +} + +CrosSettingsProvider* CrosSettings::GetProvider( + const std::string& path) const { + for (size_t i = 0; i < providers_.size(); ++i) { + if (providers_[i]->HandlesSetting(path)){ + return providers_[i]; + } + } + return NULL; +} + +void CrosSettings::Set(const std::string& path, Value* in_value) { + DCHECK(CalledOnValidThread()); + CrosSettingsProvider* provider; + provider = GetProvider(path); + if (provider) { + provider->Set(path, in_value); + } +} + +bool CrosSettings::Get(const std::string& path, Value** out_value) const { + DCHECK(CalledOnValidThread()); + CrosSettingsProvider* provider; + provider = GetProvider(path); + if (provider) { + return provider->Get(path, out_value); + } + return false; +} + +bool CrosSettings::GetInteger(const std::string& path, int* out_value) const { + DCHECK(CalledOnValidThread()); Value* value; if (!Get(path, &value)) return false; @@ -54,8 +176,9 @@ bool CrosSettings::GetInteger(const std::wstring& path, return value->GetAsInteger(out_value); } -bool CrosSettings::GetReal(const std::wstring& path, +bool CrosSettings::GetReal(const std::string& path, double* out_value) const { + DCHECK(CalledOnValidThread()); Value* value; if (!Get(path, &value)) return false; @@ -63,8 +186,9 @@ bool CrosSettings::GetReal(const std::wstring& path, return value->GetAsReal(out_value); } -bool CrosSettings::GetString(const std::wstring& path, +bool CrosSettings::GetString(const std::string& path, std::string* out_value) const { + DCHECK(CalledOnValidThread()); Value* value; if (!Get(path, &value)) return false; @@ -72,4 +196,11 @@ bool CrosSettings::GetString(const std::wstring& path, return value->GetAsString(out_value); } +CrosSettings::CrosSettings() { +} + +CrosSettings::~CrosSettings() { + DCHECK(!providers_.size()); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings.h b/chrome/browser/chromeos/cros_settings.h index cfe4ec8..226ec08 100644 --- a/chrome/browser/chromeos/cros_settings.h +++ b/chrome/browser/chromeos/cros_settings.h @@ -4,45 +4,85 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SETTINGS_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SETTINGS_H_ +#pragma once #include <string> -#include "base/values.h" +#include <vector> +#include "base/hash_tables.h" +#include "base/non_thread_safe.h" +#include "base/observer_list.h" +#include "base/singleton.h" #include "chrome/browser/chromeos/cros_settings_names.h" +#include "chrome/common/notification_observer.h" + +class Value; namespace chromeos { +class CrosSettingsProvider; + // A class manages per-device/global settings. -class CrosSettings { +class CrosSettings : public NonThreadSafe { public: // Class factory. static CrosSettings* Get(); // Helper function to test if given path is a value cros settings name. - static bool IsCrosSettings(const std::wstring& path); + static bool IsCrosSettings(const std::string& path); // Sets |in_value| to given |path| in cros settings. // Note that this takes ownership of |in_value|. - virtual void Set(const std::wstring& path, Value* in_value) = 0; + void Set(const std::string& path, Value* in_value); + + // Fires system setting change notification. + void FireObservers(const char* path); // Gets settings value of given |path| to |out_value|. - // Note that |out_value| is still owned by this class. - virtual bool Get(const std::wstring& path, Value** out_value) const = 0; + // Note that the caller owns |out_value| returned. + bool Get(const std::string& path, Value** out_value) const; // Convenience forms of Set(). These methods will replace any existing // value at that path, even if it has a different type. - void SetBoolean(const std::wstring& path, bool in_value); - void SetInteger(const std::wstring& path, int in_value); - void SetReal(const std::wstring& path, double in_value); - void SetString(const std::wstring& path, const std::string& in_value); + void SetBoolean(const std::string& path, bool in_value); + void SetInteger(const std::string& path, int in_value); + void SetReal(const std::string& path, double in_value); + void SetString(const std::string& path, const std::string& in_value); // These are convenience forms of Get(). The value will be retrieved // and the return value will be true if the path is valid and the value at // the end of the path can be returned in the form specified. - bool GetBoolean(const std::wstring& path, bool* out_value) const; - bool GetInteger(const std::wstring& path, int* out_value) const; - bool GetReal(const std::wstring& path, double* out_value) const; - bool GetString(const std::wstring& path, std::string* out_value) const; + bool GetBoolean(const std::string& path, bool* out_value) const; + bool GetInteger(const std::string& path, int* out_value) const; + bool GetReal(const std::string& path, double* out_value) const; + bool GetString(const std::string& path, std::string* out_value) const; + + // adding/removing of providers + bool AddSettingsProvider(CrosSettingsProvider* provider); + bool RemoveSettingsProvider(CrosSettingsProvider* provider); + + // If the pref at the given path changes, we call the observer's Observe + // method with PREF_CHANGED. + void AddSettingsObserver(const char* path, NotificationObserver* obs); + void RemoveSettingsObserver(const char* path, NotificationObserver* obs); + + private: + // List of ChromeOS system settings providers. + std::vector<CrosSettingsProvider*> providers_; + + // A map from settings names to a list of observers. Observers get fired in + // the order they are added. + typedef ObserverList<NotificationObserver> NotificationObserverList; + typedef base::hash_map<std::string, NotificationObserverList*> + SettingsObserverMap; + SettingsObserverMap settings_observers_; + + CrosSettings(); + ~CrosSettings(); + CrosSettingsProvider* GetProvider(const std::string& path) const; + friend struct DefaultSingletonTraits<CrosSettings>; + + DISALLOW_COPY_AND_ASSIGN(CrosSettings); }; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings_names.cc b/chrome/browser/chromeos/cros_settings_names.cc index 84fbf7e..1624726 100644 --- a/chrome/browser/chromeos/cros_settings_names.cc +++ b/chrome/browser/chromeos/cros_settings_names.cc @@ -6,10 +6,13 @@ namespace chromeos { -const wchar_t kCrosSettingsPrefix[] = L"cros."; +const char kCrosSettingsPrefix[] = "cros."; -const wchar_t kAccountsPrefAllowBWSI[] = L"cros.accounts.allowBWSI"; -const wchar_t kAccountsPrefAllowGuest[] = L"cros.accounts.allowGuest"; -const wchar_t kAccountsPrefUsers[] = L"cros.accounts.users"; +const char kAccountsPrefAllowBWSI[] = "cros.accounts.allowBWSI"; +const char kAccountsPrefAllowGuest[] = "cros.accounts.allowGuest"; +const char kAccountsPrefShowUserNamesOnSignIn[] + = "cros.accounts.showUserNamesOnSignIn"; +const char kAccountsPrefUsers[] = "cros.accounts.users"; +const char kSystemTimezone[] = "cros.system.timezone"; } // namespace chromeos diff --git a/chrome/browser/chromeos/cros_settings_names.h b/chrome/browser/chromeos/cros_settings_names.h index ce99258..6f2bb5a 100644 --- a/chrome/browser/chromeos/cros_settings_names.h +++ b/chrome/browser/chromeos/cros_settings_names.h @@ -4,15 +4,18 @@ #ifndef CHROME_BROWSER_CHROMEOS_CROS_SETTINGS_NAMES_H_ #define CHROME_BROWSER_CHROMEOS_CROS_SETTINGS_NAMES_H_ +#pragma once namespace chromeos { -extern const wchar_t kCrosSettingsPrefix[]; +extern const char kCrosSettingsPrefix[]; -extern const wchar_t kAccountsPrefAllowBWSI[]; -extern const wchar_t kAccountsPrefAllowGuest[]; -extern const wchar_t kAccountsPrefUsers[]; +extern const char kAccountsPrefAllowBWSI[]; +extern const char kAccountsPrefAllowGuest[]; +extern const char kAccountsPrefShowUserNamesOnSignIn[]; +extern const char kAccountsPrefUsers[]; +extern const char kSystemTimezone[]; } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_CROS_SETTINGS_NAMES_H_ diff --git a/chrome/browser/chromeos/customization_document.cc b/chrome/browser/chromeos/customization_document.cc index 19410ec..b9a4f34 100644 --- a/chrome/browser/chromeos/customization_document.cc +++ b/chrome/browser/chromeos/customization_document.cc @@ -6,33 +6,32 @@ #include <string> -#include "base/file_path.h" #include "base/file_util.h" #include "base/json/json_reader.h" #include "base/logging.h" -#include "base/string_util.h" +#include "base/string_number_conversions.h" #include "base/values.h" // Manifest attributes names. namespace { -const wchar_t kVersionAttr[] = L"version"; -const wchar_t kProductSkuAttr[] = L"product_sku"; -const wchar_t kInitialLocaleAttr[] = L"initial_locale"; -const wchar_t kInitialTimezoneAttr[] = L"initial_timezone"; -const wchar_t kBackgroundColorAttr[] = L"background_color"; -const wchar_t kRegistrationUrlAttr[] = L"registration_url"; -const wchar_t kSetupContentAttr[] = L"setup_content"; -const wchar_t kContentLocaleAttr[] = L"content_locale"; -const wchar_t kHelpPageAttr[] = L"help_page"; -const wchar_t kEulaPageAttr[] = L"eula_page"; -const wchar_t kAppMenuAttr[] = L"app_menu"; -const wchar_t kInitialStartPageAttr[] = L"initial_start_page"; -const wchar_t kSectionTitleAttr[] = L"section_title"; -const wchar_t kWebAppsAttr[] = L"web_apps"; -const wchar_t kSupportPageAttr[] = L"support_page"; -const wchar_t kExtensionsAttr[] = L"extensions"; +const char kVersionAttr[] = "version"; +const char kProductSkuAttr[] = "product_sku"; +const char kInitialLocaleAttr[] = "initial_locale"; +const char kInitialTimezoneAttr[] = "initial_timezone"; +const char kBackgroundColorAttr[] = "background_color"; +const char kRegistrationUrlAttr[] = "registration_url"; +const char kSetupContentAttr[] = "setup_content"; +const char kContentLocaleAttr[] = "content_locale"; +const char kHelpPageAttr[] = "help_page"; +const char kEulaPageAttr[] = "eula_page"; +const char kAppMenuAttr[] = "app_menu"; +const char kInitialStartPageAttr[] = "initial_start_page"; +const char kSectionTitleAttr[] = "section_title"; +const char kWebAppsAttr[] = "web_apps"; +const char kSupportPageAttr[] = "support_page"; +const char kExtensionsAttr[] = "extensions"; const char kAcceptedManifestVersion[] = "1.0"; @@ -72,6 +71,16 @@ bool CustomizationDocument::ParseFromJsonValue(const DictionaryValue* root) { // StartupCustomizationDocument implementation. +bool StartupCustomizationDocument::LoadManifestFromFile( + const FilePath& manifest_path) { + if (CustomizationDocument::LoadManifestFromFile(manifest_path)) { + manifest_path_ = manifest_path; + return true; + } else { + return false; + } +} + bool StartupCustomizationDocument::ParseFromJsonValue( const DictionaryValue* root) { if (!CustomizationDocument::ParseFromJsonValue(root)) @@ -93,8 +102,9 @@ bool StartupCustomizationDocument::ParseFromJsonValue( root->GetString(kBackgroundColorAttr, &background_color_string); if (!background_color_string.empty()) { if (background_color_string[0] == '#') { - background_color_ = static_cast<SkColor>( - 0xff000000 | HexStringToInt(background_color_string.substr(1))); + int background_int; + base::HexStringToInt(background_color_string.substr(1), &background_int); + background_color_ = static_cast<SkColor>(0xff000000 | background_int); } else { // Literal color constants are not supported yet. return false; @@ -128,14 +138,14 @@ bool StartupCustomizationDocument::ParseFromJsonValue( return true; } -const StartupCustomizationDocument::SetupContent* - StartupCustomizationDocument::GetSetupContent( - const std::string& locale) const { +FilePath StartupCustomizationDocument::GetSetupPagePath( + const std::string& locale, std::string SetupContent::* page_path) const { SetupContentMap::const_iterator content_iter = setup_content_.find(locale); if (content_iter != setup_content_.end()) { - return &content_iter->second; + return manifest_path_.DirName().Append(content_iter->second.*page_path); + } else { + return FilePath(); } - return NULL; } // ServicesCustomizationDocument implementation. diff --git a/chrome/browser/chromeos/customization_document.h b/chrome/browser/chromeos/customization_document.h index 169adb9..ffdd74f 100644 --- a/chrome/browser/chromeos/customization_document.h +++ b/chrome/browser/chromeos/customization_document.h @@ -4,18 +4,18 @@ #ifndef CHROME_BROWSER_CHROMEOS_CUSTOMIZATION_DOCUMENT_H_ #define CHROME_BROWSER_CHROMEOS_CUSTOMIZATION_DOCUMENT_H_ +#pragma once #include <map> #include <string> #include <vector> #include "base/basictypes.h" -#include "base/scoped_ptr.h" +#include "base/file_path.h" #include "third_party/skia/include/core/SkColor.h" class DictionaryValue; class ListValue; -class FilePath; namespace chromeos { @@ -35,6 +35,7 @@ class CustomizationDocument { // Parses manifest's attributes from the JSON dictionary value. virtual bool ParseFromJsonValue(const DictionaryValue* root); + private: // Manifest version string. std::string version_; @@ -45,6 +46,20 @@ class CustomizationDocument { class StartupCustomizationDocument : public CustomizationDocument { public: + StartupCustomizationDocument() {} + + virtual bool LoadManifestFromFile(const FilePath& manifest_path); + + const std::string& product_sku() const { return product_sku_; } + const std::string& initial_locale() const { return initial_locale_; } + const std::string& initial_timezone() const { return initial_timezone_; } + SkColor background_color() const { return background_color_; } + const std::string& registration_url() const { return registration_url_; } + + FilePath GetHelpPagePath(const std::string& locale) const; + FilePath GetEULAPagePath(const std::string& locale) const; + + private: struct SetupContent { SetupContent() {} SetupContent(const std::string& help_page_path, @@ -60,18 +75,9 @@ class StartupCustomizationDocument : public CustomizationDocument { typedef std::map<std::string, SetupContent> SetupContentMap; - StartupCustomizationDocument() {} - - const std::string& product_sku() const { return product_sku_; } - const std::string& initial_locale() const { return initial_locale_; } - const std::string& initial_timezone() const { return initial_timezone_; } - SkColor background_color() const { return background_color_; } - const std::string& registration_url() const { return registration_url_; } - - const SetupContent* GetSetupContent(const std::string& locale) const; - - protected: virtual bool ParseFromJsonValue(const DictionaryValue* root); + FilePath GetSetupPagePath(const std::string& locale, + std::string SetupContent::* page_path) const; // Product SKU. std::string product_sku_; @@ -91,9 +97,21 @@ class StartupCustomizationDocument : public CustomizationDocument { // Setup content for different locales. SetupContentMap setup_content_; + // Copy of manifest full path. + FilePath manifest_path_; + DISALLOW_COPY_AND_ASSIGN(StartupCustomizationDocument); }; +inline FilePath StartupCustomizationDocument::GetHelpPagePath( + const std::string& locale) const { + return GetSetupPagePath(locale, &SetupContent::help_page_path); +} +inline FilePath StartupCustomizationDocument::GetEULAPagePath( + const std::string& locale) const { + return GetSetupPagePath(locale, &SetupContent::eula_page_path); +} + // OEM services customization document class. class ServicesCustomizationDocument : public CustomizationDocument { @@ -114,7 +132,7 @@ class ServicesCustomizationDocument : public CustomizationDocument { const StringList& web_apps() const { return web_apps_; } const StringList& extensions() const { return extensions_; } - protected: + private: virtual bool ParseFromJsonValue(const DictionaryValue* root); bool ParseStringListFromJsonValue(const ListValue* list_value, diff --git a/chrome/browser/chromeos/customization_document_unittest.cc b/chrome/browser/chromeos/customization_document_unittest.cc index 41c2cee..a19e63a 100644 --- a/chrome/browser/chromeos/customization_document_unittest.cc +++ b/chrome/browser/chromeos/customization_document_unittest.cc @@ -104,13 +104,13 @@ TEST_F(StartupCustomizationDocumentTest, LoadGoodManifestFromString) { EXPECT_EQ(customization_.background_color(), SkColorSetRGB(0x88, 0x00, 0x88)); EXPECT_EQ(customization_.registration_url(), "http://www.google.com"); - EXPECT_EQ(customization_.GetSetupContent("en_US")->help_page_path, + EXPECT_EQ(customization_.GetHelpPagePath("en_US").value(), "setup_content/en_US/help.html"); - EXPECT_EQ(customization_.GetSetupContent("en_US")->eula_page_path, + EXPECT_EQ(customization_.GetEULAPagePath("en_US").value(), "setup_content/en_US/eula.html"); - EXPECT_EQ(customization_.GetSetupContent("ru")->help_page_path, + EXPECT_EQ(customization_.GetHelpPagePath("ru").value(), "setup_content/ru/help.html"); - EXPECT_EQ(customization_.GetSetupContent("ru")->eula_page_path, + EXPECT_EQ(customization_.GetEULAPagePath("ru").value(), "setup_content/ru/eula.html"); } diff --git a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc index 9ca1730..249419a 100644 --- a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc @@ -7,40 +7,73 @@ #include "app/l10n_util.h" #include "base/json/json_reader.h" #include "base/scoped_ptr.h" +#include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/chromeos/cros_settings_provider_user.h" +#include "chrome/browser/chromeos/login/user_manager.h" #include "grit/generated_resources.h" namespace chromeos { -AccountsOptionsHandler::AccountsOptionsHandler() { +AccountsOptionsHandler::AccountsOptionsHandler() + : CrosOptionsPageUIHandler(new UserCrosSettingsProvider()) { } AccountsOptionsHandler::~AccountsOptionsHandler() { } +void AccountsOptionsHandler::RegisterMessages() { + DCHECK(dom_ui_); + dom_ui_->RegisterMessageCallback("whitelistUser", + NewCallback(this, &AccountsOptionsHandler::WhitelistUser)); + dom_ui_->RegisterMessageCallback("unwhitelistUser", + NewCallback(this, &AccountsOptionsHandler::UnwhitelistUser)); +} + void AccountsOptionsHandler::GetLocalizedValues( DictionaryValue* localized_strings) { DCHECK(localized_strings); - localized_strings->SetString(L"accountsPage", l10n_util::GetString( + localized_strings->SetString("accountsPage", l10n_util::GetStringUTF16( IDS_OPTIONS_ACCOUNTS_TAB_LABEL)); - localized_strings->SetString(L"allow_BWSI", l10n_util::GetString( + localized_strings->SetString("allow_BWSI", l10n_util::GetStringUTF16( IDS_OPTIONS_ACCOUNTS_ALLOW_BWSI_DESCRIPTION)); - localized_strings->SetString(L"allow_guest",l10n_util::GetString( + localized_strings->SetString("allow_guest",l10n_util::GetStringUTF16( IDS_OPTIONS_ACCOUNTS_ALLOW_GUEST_DESCRIPTION)); - localized_strings->SetString(L"user_list_title",l10n_util::GetString( - IDS_OPTIONS_ACCOUNTS_USER_LIST_TITLE)); - localized_strings->SetString(L"add_user",l10n_util::GetString( - IDS_OPTIONS_ACCOUNTS_ADD_USER)); - localized_strings->SetString(L"remove_user",l10n_util::GetString( - IDS_OPTIONS_ACCOUNTS_REMOVE_USER)); - localized_strings->SetString(L"add_user_email",l10n_util::GetString( - IDS_OPTIONS_ACCOUNTS_EMAIL_LABEL)); - - localized_strings->SetString(L"ok_label",l10n_util::GetString( - IDS_OK)); - localized_strings->SetString(L"cancel_label",l10n_util::GetString( - IDS_CANCEL)); + localized_strings->SetString("show_user_on_signin",l10n_util::GetStringUTF16( + IDS_OPTIONS_ACCOUNTS_SHOW_USER_NAMES_ON_SINGIN_DESCRIPTION)); + localized_strings->SetString("username_edit_hint",l10n_util::GetStringUTF16( + IDS_OPTIONS_ACCOUNTS_USERNAME_EDIT_HINT)); + localized_strings->SetString("username_format",l10n_util::GetStringUTF16( + IDS_OPTIONS_ACCOUNTS_USERNAME_FORMAT)); + localized_strings->SetString("add_users",l10n_util::GetStringUTF16( + IDS_OPTIONS_ACCOUNTS_ADD_USERS)); + + localized_strings->SetString("current_user_is_owner", + UserManager::Get()->current_user_is_owner() ? + ASCIIToUTF16("true") : ASCIIToUTF16("false")); +} + +UserCrosSettingsProvider* AccountsOptionsHandler::users_settings() const { + return static_cast<UserCrosSettingsProvider*>(settings_provider_.get()); +} + +void AccountsOptionsHandler::WhitelistUser(const ListValue* args) { + std::string email; + if (!args->GetString(0, &email)) { + return; + } + + users_settings()->WhitelistUser(email); +} + +void AccountsOptionsHandler::UnwhitelistUser(const ListValue* args) { + std::string email; + if (!args->GetString(0, &email)) { + return; + } + + users_settings()->UnwhitelistUser(email); } } // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/accounts_options_handler.h b/chrome/browser/chromeos/dom_ui/accounts_options_handler.h index 0de8000..a43ec50 100644 --- a/chrome/browser/chromeos/dom_ui/accounts_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/accounts_options_handler.h @@ -4,21 +4,33 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_ACCOUNTS_OPTIONS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_ACCOUNTS_OPTIONS_HANDLER_H_ +#pragma once -#include "chrome/browser/dom_ui/options_ui.h" +#include "chrome/browser/chromeos/dom_ui/cros_options_page_ui_handler.h" namespace chromeos { +class UserCrosSettingsProvider; + // ChromeOS accounts options page handler. -class AccountsOptionsHandler : public OptionsPageUIHandler { +class AccountsOptionsHandler : public CrosOptionsPageUIHandler { public: AccountsOptionsHandler(); virtual ~AccountsOptionsHandler(); + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + // OptionsUIHandler implementation: virtual void GetLocalizedValues(DictionaryValue* localized_strings); private: + UserCrosSettingsProvider* users_settings() const; + + // Javascript callbacks to whitelist/unwhitelist user. + void WhitelistUser(const ListValue* args); + void UnwhitelistUser(const ListValue* args); + DISALLOW_COPY_AND_ASSIGN(AccountsOptionsHandler); }; diff --git a/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.cc b/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.cc index 889a85d..d76e33d 100644 --- a/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.cc @@ -5,33 +5,42 @@ #include "chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.h" #include "base/json/json_reader.h" -#include "base/string_util.h" +#include "base/string_number_conversions.h" #include "chrome/browser/chromeos/cros_settings.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/common/notification_service.h" namespace chromeos { -Value* CoreChromeOSOptionsHandler::FetchPref(const std::wstring& pref_name) { +CoreChromeOSOptionsHandler::CoreChromeOSOptionsHandler() + : handling_change_(false) { +} + +Value* CoreChromeOSOptionsHandler::FetchPref(const std::string& pref_name) { if (!CrosSettings::IsCrosSettings(pref_name)) return ::CoreOptionsHandler::FetchPref(pref_name); Value* pref_value = NULL; CrosSettings::Get()->Get(pref_name, &pref_value); - return pref_value ? pref_value->DeepCopy() : Value::CreateNullValue(); + return pref_value ? pref_value : Value::CreateNullValue(); } -void CoreChromeOSOptionsHandler::ObservePref(const std::wstring& pref_name) { +void CoreChromeOSOptionsHandler::ObservePref(const std::string& pref_name) { if (!CrosSettings::IsCrosSettings(pref_name)) return ::CoreOptionsHandler::ObservePref(pref_name); // TODO(xiyuan): Change this when CrosSettings supports observers. + CrosSettings::Get()->AddSettingsObserver(pref_name.c_str(), this); } -void CoreChromeOSOptionsHandler::SetPref(const std::wstring& pref_name, +void CoreChromeOSOptionsHandler::SetPref(const std::string& pref_name, Value::ValueType pref_type, - const std::string& value_string) { + const std::string& value_string, + const std::string& metric) { if (!CrosSettings::IsCrosSettings(pref_name)) - return ::CoreOptionsHandler::SetPref(pref_name, pref_type, value_string); - + return ::CoreOptionsHandler::SetPref(pref_name, pref_type, value_string, + metric); + handling_change_ = true; CrosSettings* cros_settings = CrosSettings::Get(); switch (pref_type) { case Value::TYPE_BOOLEAN: @@ -39,7 +48,7 @@ void CoreChromeOSOptionsHandler::SetPref(const std::wstring& pref_name, break; case Value::TYPE_INTEGER: int int_value; - if (StringToInt(value_string, &int_value)) + if (base::StringToInt(value_string, &int_value)) cros_settings->SetInteger(pref_name, int_value); break; case Value::TYPE_STRING: @@ -55,6 +64,54 @@ void CoreChromeOSOptionsHandler::SetPref(const std::wstring& pref_name, break; } } + handling_change_ = false; + + ProcessUserMetric(pref_type, value_string, metric); +} + +void CoreChromeOSOptionsHandler::StopObservingPref(const std::string& path) { + // Unregister this instance from observing prefs of chrome os settings. + if (CrosSettings::IsCrosSettings(path)) + CrosSettings::Get()->RemoveSettingsObserver(path.c_str(), this); + else // Call base class to handle regular preferences. + ::CoreOptionsHandler::StopObservingPref(path); +} + +void CoreChromeOSOptionsHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + // Ignore the notification if this instance had caused it. + if (handling_change_) + return; + if (type == NotificationType::SYSTEM_SETTING_CHANGED) { + NotifySettingsChanged(Details<std::string>(details).ptr()); + return; + } + ::CoreOptionsHandler::Observe(type, source, details); +} + +void CoreChromeOSOptionsHandler::NotifySettingsChanged( + const std::string* setting_name) { + DCHECK(dom_ui_); + DCHECK(CrosSettings::Get()->IsCrosSettings(*setting_name)); + Value* value = NULL; + if (!CrosSettings::Get()->Get(*setting_name, &value)) { + NOTREACHED(); + if (value) + delete value; + return; + } + for (PreferenceCallbackMap::const_iterator iter = + pref_callback_map_.find(*setting_name); + iter != pref_callback_map_.end(); ++iter) { + const std::wstring& callback_function = iter->second; + ListValue result_value; + result_value.Append(Value::CreateStringValue(setting_name->c_str())); + result_value.Append(value->DeepCopy()); + dom_ui_->CallJavascriptFunction(callback_function, result_value); + } + if (value) + delete value; } } // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.h b/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.h index 0c4dc5c..d3b63f5 100644 --- a/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/core_chromeos_options_handler.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_CORE_CHROMEOS_OPTIONS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_CORE_CHROMEOS_OPTIONS_HANDLER_H_ +#pragma once #include "chrome/browser/dom_ui/core_options_handler.h" @@ -12,15 +13,30 @@ namespace chromeos { // CoreChromeOSOptionsHandler handles ChromeOS settings. class CoreChromeOSOptionsHandler : public ::CoreOptionsHandler { public: + CoreChromeOSOptionsHandler(); protected: - // ::CoreOptionsHandler Implementation - virtual Value* FetchPref(const std::wstring& pref_name); - virtual void ObservePref(const std::wstring& pref_name); - virtual void SetPref(const std::wstring& pref_name, + // ::CoreOptionsHandler overrides + virtual Value* FetchPref(const std::string& pref_name); + virtual void ObservePref(const std::string& pref_name); + virtual void SetPref(const std::string& pref_name, Value::ValueType pref_type, - const std::string& value_string); - + const std::string& value_string, + const std::string& metric); + virtual void StopObservingPref(const std::string& path); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // Notifies registered JS callbacks on ChromeOS setting change. + void NotifySettingsChanged(const std::string* setting_name); + + // Keeps the track of change caused by the handler to make sure + // it does not signal itself again. + bool handling_change_; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/labs_handler.cc b/chrome/browser/chromeos/dom_ui/labs_handler.cc index d6ecee0..7f45611 100644 --- a/chrome/browser/chromeos/dom_ui/labs_handler.cc +++ b/chrome/browser/chromeos/dom_ui/labs_handler.cc @@ -5,20 +5,8 @@ #include "chrome/browser/chromeos/dom_ui/labs_handler.h" #include "app/l10n_util.h" -#include "app/resource_bundle.h" -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/i18n/time_formatting.h" -#include "base/stl_util-inl.h" -#include "base/time.h" -#include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/common/notification_service.h" -#include "grit/browser_resources.h" -#include "grit/chromium_strings.h" #include "grit/generated_resources.h" -#include "grit/locale_settings.h" -#include "grit/theme_resources.h" LabsHandler::LabsHandler() { } @@ -30,15 +18,21 @@ void LabsHandler::GetLocalizedValues( DictionaryValue* localized_strings) { DCHECK(localized_strings); // Labs page - ChromeOS - localized_strings->SetString(L"labsPage", - l10n_util::GetString(IDS_OPTIONS_LABS_TAB_LABEL)); - localized_strings->SetString(L"mediaplayer_title", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_MEDIAPLAYER)); - localized_strings->SetString(L"mediaplayer", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_MEDIAPLAYER_DESCRIPTION)); - - localized_strings->SetString(L"advanced_file_title", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_ADVANCEDFS)); - localized_strings->SetString(L"advanced_filesystem", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_ADVANCEDFS_DESCRIPTION)); + localized_strings->SetString("labsPage", + l10n_util::GetStringUTF16(IDS_OPTIONS_LABS_TAB_LABEL)); + + localized_strings->SetString("mediaplayer_title", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_MEDIAPLAYER)); + localized_strings->SetString("mediaplayer", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_MEDIAPLAYER_DESCRIPTION)); + + localized_strings->SetString("advanced_file_title", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_ADVANCEDFS)); + localized_strings->SetString("advanced_filesystem", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_ADVANCEDFS_DESCRIPTION)); + + localized_strings->SetString("talk_title", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_TALK)); + localized_strings->SetString("talk", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_TALK_DESCRIPTION)); } diff --git a/chrome/browser/chromeos/dom_ui/labs_handler.h b/chrome/browser/chromeos/dom_ui/labs_handler.h index 5c217bb..3fc1121 100644 --- a/chrome/browser/chromeos/dom_ui/labs_handler.h +++ b/chrome/browser/chromeos/dom_ui/labs_handler.h @@ -4,8 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_LABS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_LABS_HANDLER_H_ - -#include <string> +#pragma once #include "chrome/browser/dom_ui/options_ui.h" @@ -19,7 +18,6 @@ class LabsHandler : public OptionsPageUIHandler { virtual void GetLocalizedValues(DictionaryValue* localized_strings); private: - DISALLOW_COPY_AND_ASSIGN(LabsHandler); }; diff --git a/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.cc b/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.cc index 150ae15..46a8e2d 100644 --- a/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.cc @@ -5,10 +5,13 @@ #include "chrome/browser/chromeos/dom_ui/language_hangul_options_handler.h" #include "app/l10n_util.h" +#include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/chromeos/language_preferences.h" #include "grit/generated_resources.h" +namespace chromeos { + LanguageHangulOptionsHandler::LanguageHangulOptionsHandler() { } @@ -19,21 +22,23 @@ void LanguageHangulOptionsHandler::GetLocalizedValues( DictionaryValue* localized_strings) { DCHECK(localized_strings); // Language Hangul page - ChromeOS - localized_strings->SetString(L"keyboard_layout", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_KEYBOARD_LAYOUT_TEXT)); + localized_strings->SetString("hangul_keyboard_layout", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_KEYBOARD_LAYOUT_TEXT)); - localized_strings->Set(L"keyboardLayoutList", GetKeyboardLayoutList()); + localized_strings->Set("HangulkeyboardLayoutList", GetKeyboardLayoutList()); } ListValue* LanguageHangulOptionsHandler::GetKeyboardLayoutList() { ListValue* keyboard_layout_list = new ListValue(); - for (size_t i = 0; i < arraysize(chromeos::kHangulKeyboardNameIDPairs); ++i) { + for (size_t i = 0; i < language_prefs::kNumHangulKeyboardNameIDPairs; ++i) { ListValue* option = new ListValue(); - option->Append(Value::CreateStringValue(ASCIIToWide( - chromeos::kHangulKeyboardNameIDPairs[i].keyboard_id))); - option->Append(Value::CreateStringValue(l10n_util::GetString( - chromeos::kHangulKeyboardNameIDPairs[i].message_id))); + option->Append(Value::CreateStringValue( + language_prefs::kHangulKeyboardNameIDPairs[i].keyboard_id)); + option->Append(Value::CreateStringValue(l10n_util::GetStringUTF16( + language_prefs::kHangulKeyboardNameIDPairs[i].message_id))); keyboard_layout_list->Append(option); } return keyboard_layout_list; } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.h b/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.h index 1872fbc..cedfb05 100644 --- a/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/language_hangul_options_handler.h @@ -4,12 +4,15 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_HANGUL_OPTIONS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_HANGUL_OPTIONS_HANDLER_H_ +#pragma once #include "chrome/browser/dom_ui/options_ui.h" class DictionaryValue; class ListValue; +namespace chromeos { + // Hangul options page UI handler. class LanguageHangulOptionsHandler : public OptionsPageUIHandler { public: @@ -26,4 +29,6 @@ class LanguageHangulOptionsHandler : public OptionsPageUIHandler { DISALLOW_COPY_AND_ASSIGN(LanguageHangulOptionsHandler); }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_HANGUL_OPTIONS_HANDLER_H_ diff --git a/chrome/browser/chromeos/dom_ui/language_options_handler.cc b/chrome/browser/chromeos/dom_ui/language_options_handler.cc index c78cccc..594f8b1 100644 --- a/chrome/browser/chromeos/dom_ui/language_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/language_options_handler.cc @@ -4,12 +4,29 @@ #include "chrome/browser/chromeos/dom_ui/language_options_handler.h" +#include <map> +#include <set> +#include <string> +#include <utility> + #include "app/l10n_util.h" -#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/input_method_library.h" +#include "chrome/browser/chromeos/input_method/input_method_util.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/pref_names.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +namespace chromeos { + LanguageOptionsHandler::LanguageOptionsHandler() { } @@ -19,26 +36,248 @@ LanguageOptionsHandler::~LanguageOptionsHandler() { void LanguageOptionsHandler::GetLocalizedValues( DictionaryValue* localized_strings) { DCHECK(localized_strings); - localized_strings->SetString(L"languagePage", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_DIALOG_TITLE)); - localized_strings->SetString(L"add_button", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_BUTTON)); - localized_strings->SetString(L"configure", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE)); - localized_strings->SetString(L"input_method", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD)); - localized_strings->SetString(L"languages", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_LANGUAGES)); - localized_strings->SetString(L"remove_button", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_REMOVE_BUTTON)); - localized_strings->SetString(L"others", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS)); - localized_strings->SetString(L"is_displayed_in_this_language", - l10n_util::GetStringF( + localized_strings->SetString("languagePage", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_DIALOG_TITLE)); + localized_strings->SetString("add_button", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_BUTTON)); + localized_strings->SetString("configure", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE)); + localized_strings->SetString("input_method", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD)); + localized_strings->SetString("languages", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_LANGUAGES)); + localized_strings->SetString("please_add_another_input_method", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_PLEASE_ADD_ANOTHER_INPUT_METHOD)); + localized_strings->SetString("please_add_another_language", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_PLEASE_ADD_ANOTHER_LANGUAGE)); + localized_strings->SetString("ok_button", l10n_util::GetStringUTF16(IDS_OK)); + localized_strings->SetString("remove_button", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_REMOVE_BUTTON)); + localized_strings->SetString("restart_button", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_RESTART_BUTTON)); + localized_strings->SetString("add_language_instructions", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_INSTRUCTIONS)); + localized_strings->SetString("input_method_instructions", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_INSTRUCTIONS)); + localized_strings->SetString("switch_input_methods_hint", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_SWITCH_INPUT_METHODS_HINT, + ASCIIToUTF16("alt+shift"))); + localized_strings->SetString("select_previous_input_method_hint", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_SELECT_PREVIOUS_INPUT_METHOD_HINT, + ASCIIToUTF16("ctrl+space"))); + localized_strings->SetString("cannot_be_displayed_in_this_language", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_CANNOT_BE_DISPLAYED_IN_THIS_LANGUAGE, + l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME))); + localized_strings->SetString("is_displayed_in_this_language", + l10n_util::GetStringFUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE, - l10n_util::GetString(IDS_PRODUCT_OS_NAME))); - localized_strings->SetString(L"display_in_this_language", - l10n_util::GetStringF( + l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME))); + localized_strings->SetString("display_in_this_language", + l10n_util::GetStringFUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE, - l10n_util::GetString(IDS_PRODUCT_OS_NAME))); + l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME))); + localized_strings->SetString("restart_required", + l10n_util::GetStringUTF16(IDS_OPTIONS_RESTART_REQUIRED)); + localized_strings->SetString("this_language_is_currently_in_use", + l10n_util::GetStringFUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_THIS_LANGUAGE_IS_CURRENTLY_IN_USE, + l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME))); + + // GetSupportedInputMethods() never return NULL. + scoped_ptr<InputMethodDescriptors> descriptors( + CrosLibrary::Get()->GetInputMethodLibrary()->GetSupportedInputMethods()); + + // The followigns are resources, rather than local strings. + localized_strings->SetString("currentUiLanguageCode", + g_browser_process->GetApplicationLocale()); + localized_strings->Set("inputMethodList", GetInputMethodList(*descriptors)); + localized_strings->Set("languageList", GetLanguageList(*descriptors)); + localized_strings->Set("uiLanguageCodeSet", GetUiLanguageCodeSet()); +} + +void LanguageOptionsHandler::RegisterMessages() { + DCHECK(dom_ui_); + dom_ui_->RegisterMessageCallback("inputMethodDisable", + NewCallback(this, &LanguageOptionsHandler::InputMethodDisableCallback)); + dom_ui_->RegisterMessageCallback("inputMethodEnable", + NewCallback(this, &LanguageOptionsHandler::InputMethodEnableCallback)); + dom_ui_->RegisterMessageCallback("inputMethodOptionsOpen", + NewCallback(this, + &LanguageOptionsHandler::InputMethodOptionsOpenCallback)); + dom_ui_->RegisterMessageCallback("languageOptionsOpen", + NewCallback(this, &LanguageOptionsHandler::LanguageOptionsOpenCallback)); + dom_ui_->RegisterMessageCallback("uiLanguageChange", + NewCallback(this, &LanguageOptionsHandler::UiLanguageChangeCallback)); + dom_ui_->RegisterMessageCallback("uiLanguageRestart", + NewCallback(this, &LanguageOptionsHandler::RestartCallback)); } + +ListValue* LanguageOptionsHandler::GetInputMethodList( + const InputMethodDescriptors& descriptors) { + ListValue* input_method_list = new ListValue(); + + for (size_t i = 0; i < descriptors.size(); ++i) { + const InputMethodDescriptor& descriptor = descriptors[i]; + const std::string language_code = + input_method::GetLanguageCodeFromDescriptor(descriptor); + const std::string display_name = + input_method::GetInputMethodDisplayNameFromId(descriptor.id); + + DictionaryValue* dictionary = new DictionaryValue(); + dictionary->SetString("id", descriptor.id); + dictionary->SetString("displayName", display_name); + + // One input method can be associated with multiple languages, hence + // we use a dictionary here. + DictionaryValue* language_codes = new DictionaryValue(); + language_codes->SetBoolean(language_code, true); + // Check kExtraLanguages to see if there are languages associated with + // this input method. If these are present, add these. + for (size_t j = 0; j < arraysize(input_method::kExtraLanguages); ++j) { + const std::string extra_input_method_id = + input_method::kExtraLanguages[j].input_method_id; + const std::string extra_language_code = + input_method::kExtraLanguages[j].language_code; + if (extra_input_method_id == descriptor.id) { + language_codes->SetBoolean(extra_language_code, true); + } + } + dictionary->Set("languageCodeSet", language_codes); + + input_method_list->Append(dictionary); + } + + return input_method_list; +} + +ListValue* LanguageOptionsHandler::GetLanguageList( + const InputMethodDescriptors& descriptors) { + std::set<std::string> language_codes; + // Collect the language codes from the supported input methods. + for (size_t i = 0; i < descriptors.size(); ++i) { + const InputMethodDescriptor& descriptor = descriptors[i]; + const std::string language_code = + input_method::GetLanguageCodeFromDescriptor(descriptor); + language_codes.insert(language_code); + } + // Collect the language codes from kExtraLanguages. + for (size_t i = 0; i < arraysize(input_method::kExtraLanguages); ++i) { + const char* language_code = + input_method::kExtraLanguages[i].language_code; + language_codes.insert(language_code); + } + + // Map of display name -> {language code, native_display_name}. + // In theory, we should be able to create a map that is sorted by + // display names using ICU comparator, but doing it is hard, thus we'll + // use an auxiliary vector to achieve the same result. + typedef std::pair<std::string, std::wstring> LanguagePair; + typedef std::map<std::wstring, LanguagePair> LanguageMap; + LanguageMap language_map; + // The auxiliary vector mentioned above. + std::vector<std::wstring> display_names; + + // Build the list of display names, and build the language map. + for (std::set<std::string>::const_iterator iter = language_codes.begin(); + iter != language_codes.end(); ++iter) { + const std::wstring display_name = + input_method::GetLanguageDisplayNameFromCode(*iter); + const std::wstring native_display_name = + input_method::GetLanguageNativeDisplayNameFromCode(*iter); + display_names.push_back(display_name); + language_map[display_name] = + std::make_pair(*iter, native_display_name); + } + DCHECK_EQ(display_names.size(), language_map.size()); + + // Sort display names using locale specific sorter. + l10n_util::SortStrings(g_browser_process->GetApplicationLocale(), + &display_names); + + // Build the language list from the language map. + ListValue* language_list = new ListValue(); + for (size_t i = 0; i < display_names.size(); ++i) { + const LanguagePair& pair = language_map[display_names[i]]; + DictionaryValue* dictionary = new DictionaryValue(); + dictionary->SetString("code", pair.first); + dictionary->SetString("displayName", WideToUTF16Hack(display_names[i])); + dictionary->SetString("nativeDisplayName", WideToUTF16Hack(pair.second)); + language_list->Append(dictionary); + } + + return language_list; +} + +DictionaryValue* LanguageOptionsHandler::GetUiLanguageCodeSet() { + DictionaryValue* dictionary = new DictionaryValue(); + const std::vector<std::string>& available_locales = + l10n_util::GetAvailableLocales(); + for (size_t i = 0; i < available_locales.size(); ++i) { + dictionary->SetBoolean(available_locales[i], true); + } + return dictionary; +} + +void LanguageOptionsHandler::InputMethodDisableCallback( + const ListValue* args) { + const std::string input_method_id = WideToASCII(ExtractStringValue(args)); + const std::string action = StringPrintf( + "LanguageOptions_DisableInputMethod_%s", input_method_id.c_str()); + UserMetrics::RecordComputedAction(action); +} + +void LanguageOptionsHandler::InputMethodEnableCallback( + const ListValue* args) { + const std::string input_method_id = WideToASCII(ExtractStringValue(args)); + const std::string action = StringPrintf( + "LanguageOptions_EnableInputMethod_%s", input_method_id.c_str()); + UserMetrics::RecordComputedAction(action); +} + +void LanguageOptionsHandler::InputMethodOptionsOpenCallback( + const ListValue* args) { + const std::string input_method_id = WideToASCII(ExtractStringValue(args)); + const std::string action = StringPrintf( + "InputMethodOptions_Open_%s", input_method_id.c_str()); + UserMetrics::RecordComputedAction(action); +} + +void LanguageOptionsHandler::LanguageOptionsOpenCallback( + const ListValue* args) { + UserMetrics::RecordAction(UserMetricsAction("LanguageOptions_Open")); +} + +void LanguageOptionsHandler::UiLanguageChangeCallback( + const ListValue* args) { + const std::string language_code = WideToASCII(ExtractStringValue(args)); + CHECK(!language_code.empty()); + const std::string action = StringPrintf( + "LanguageOptions_UiLanguageChange_%s", language_code.c_str()); + UserMetrics::RecordComputedAction(action); + + PrefService* prefs = g_browser_process->local_state(); + prefs->SetString(prefs::kApplicationLocale, language_code); + prefs->SavePersistentPrefs(); + dom_ui_->CallJavascriptFunction( + L"options.LanguageOptions.uiLanguageSaved"); +} + +void LanguageOptionsHandler::RestartCallback(const ListValue* args) { + UserMetrics::RecordAction(UserMetricsAction("LanguageOptions_Restart")); + + Browser* browser = Browser::GetBrowserForController( + &dom_ui_->tab_contents()->controller(), NULL); + // TODO(kochi): For ChromiumOS, just exiting means browser restart. + // Implement browser restart for Chromium Win/Linux/Mac. + if (browser) + browser->ExecuteCommand(IDC_EXIT); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/dom_ui/language_options_handler.h b/chrome/browser/chromeos/dom_ui/language_options_handler.h index da17a41..79ab685 100644 --- a/chrome/browser/chromeos/dom_ui/language_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/language_options_handler.h @@ -4,10 +4,15 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_OPTIONS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_OPTIONS_HANDLER_H_ +#pragma once +#include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/dom_ui/options_ui.h" class DictionaryValue; +class ListValue; + +namespace chromeos { // ChromeOS language options page UI handler. class LanguageOptionsHandler : public OptionsPageUIHandler { @@ -18,8 +23,61 @@ class LanguageOptionsHandler : public OptionsPageUIHandler { // OptionsUIHandler implementation. virtual void GetLocalizedValues(DictionaryValue* localized_strings); + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // The following static methods are public for ease of testing. + + // Gets the list of input methods from the given input descriptors. + // The return value will look like: + // [{'id': 'pinyin', 'displayName': 'Pinyin', + // 'languageCodeSet': {'zh-CW': true}}, ...] + // + // Note that true in languageCodeSet does not mean anything. We just use + // the dictionary as a set. + static ListValue* GetInputMethodList( + const InputMethodDescriptors& descriptors); + + // Gets the list of languages from the given input descriptors. + // The return value will look like: + // [{'code': 'fi', 'displayName': 'Finnish', 'nativeDisplayName': 'suomi'}, + // ...] + static ListValue* GetLanguageList(const InputMethodDescriptors& descriptors); + + // Gets the set of language codes that can be used as UI language. + // The return value will look like: + // {'en-US': true, 'fi': true, 'fr': true, ...} + // + // Note that true in languageCodeSet does not mean anything. We just use + // the dictionary as a set. + static DictionaryValue* GetUiLanguageCodeSet(); + private: + // Called when the input method is disabled. + // |args| will contain the input method ID as string (ex. "mozc"). + void InputMethodDisableCallback(const ListValue* args); + + // Called when the input method is enabled. + // |args| will contain the input method ID as string (ex. "mozc"). + void InputMethodEnableCallback(const ListValue* args); + + // Called when the input method options page is opened. + // |args| will contain the input method ID as string (ex. "mozc"). + void InputMethodOptionsOpenCallback(const ListValue* args); + + // Called when the language options is opened. + void LanguageOptionsOpenCallback(const ListValue* args); + + // Called when the UI language is changed. + // |args| will contain the language code as string (ex. "fr"). + void UiLanguageChangeCallback(const ListValue* args); + + // Called when the restart button is clicked. + void RestartCallback(const ListValue* args); + DISALLOW_COPY_AND_ASSIGN(LanguageOptionsHandler); }; +} // namespace + #endif // CHROME_BROWSER_CHROMEOS_DOM_UI_LANGUAGE_OPTIONS_HANDLER_H_ diff --git a/chrome/browser/chromeos/dom_ui/sync_options_handler.cc b/chrome/browser/chromeos/dom_ui/sync_options_handler.cc deleted file mode 100644 index 073b8d4..0000000 --- a/chrome/browser/chromeos/dom_ui/sync_options_handler.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2010 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/dom_ui/sync_options_handler.h" - -#include "app/l10n_util.h" -#include "app/resource_bundle.h" -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/i18n/time_formatting.h" -#include "base/stl_util-inl.h" -#include "base/time.h" -#include "base/utf_string_conversions.h" -#include "base/values.h" -#include "chrome/common/notification_service.h" -#include "grit/browser_resources.h" -#include "grit/chromium_strings.h" -#include "grit/generated_resources.h" -#include "grit/locale_settings.h" -#include "grit/theme_resources.h" - -SyncOptionsHandler::SyncOptionsHandler() {} - -SyncOptionsHandler::~SyncOptionsHandler() {} - -void SyncOptionsHandler::GetLocalizedValues( - DictionaryValue* localized_strings) { - DCHECK(localized_strings); - // Sync page - ChromeOS - localized_strings->SetString(L"syncPage", - l10n_util::GetString(IDS_SYNC_NTP_SYNC_SECTION_TITLE)); - localized_strings->SetString(L"sync_title", - l10n_util::GetString(IDS_CUSTOMIZE_SYNC_DESCRIPTION)); - localized_strings->SetString(L"syncsettings", - l10n_util::GetString(IDS_SYNC_DATATYPE_PREFERENCES)); - localized_strings->SetString(L"syncbookmarks", - l10n_util::GetString(IDS_SYNC_DATATYPE_BOOKMARKS)); - localized_strings->SetString(L"synctypedurls", - l10n_util::GetString(IDS_SYNC_DATATYPE_TYPED_URLS)); - localized_strings->SetString(L"syncpasswords", - l10n_util::GetString(IDS_SYNC_DATATYPE_PASSWORDS)); - localized_strings->SetString(L"syncextensions", - l10n_util::GetString(IDS_SYNC_DATATYPE_EXTENSIONS)); - localized_strings->SetString(L"syncautofill", - l10n_util::GetString(IDS_SYNC_DATATYPE_AUTOFILL)); - localized_strings->SetString(L"syncthemes", - l10n_util::GetString(IDS_SYNC_DATATYPE_THEMES)); -} diff --git a/chrome/browser/chromeos/dom_ui/sync_options_handler.h b/chrome/browser/chromeos/dom_ui/sync_options_handler.h deleted file mode 100644 index a58c583..0000000 --- a/chrome/browser/chromeos/dom_ui/sync_options_handler.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2010 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_DOM_UI_SYNC_OPTIONS_HANDLER_H_ -#define CHROME_BROWSER_CHROMEOS_DOM_UI_SYNC_OPTIONS_HANDLER_H_ - -#include "chrome/browser/dom_ui/options_ui.h" - -// ChromeOS system options page UI handler. -class SyncOptionsHandler : public OptionsPageUIHandler { - public: - SyncOptionsHandler(); - virtual ~SyncOptionsHandler(); - - // OptionsUIHandler implementation. - virtual void GetLocalizedValues(DictionaryValue* localized_strings); - - private: - DISALLOW_COPY_AND_ASSIGN(SyncOptionsHandler); -}; - -#endif // CHROME_BROWSER_CHROMEOS_DOM_UI_SYNC_OPTIONS_HANDLER_H_ diff --git a/chrome/browser/chromeos/dom_ui/system_options_handler.cc b/chrome/browser/chromeos/dom_ui/system_options_handler.cc index b280526..d47c82c 100644 --- a/chrome/browser/chromeos/dom_ui/system_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/system_options_handler.cc @@ -4,15 +4,16 @@ #include "chrome/browser/chromeos/dom_ui/system_options_handler.h" +#include <string> + #include "app/l10n_util.h" -#include "app/resource_bundle.h" #include "base/basictypes.h" #include "base/callback.h" -#include "base/i18n/time_formatting.h" -#include "base/stl_util-inl.h" -#include "base/time.h" +#include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/chromeos/dom_ui/system_settings_provider.h" +#include "chrome/browser/chromeos/language_preferences.h" #include "chrome/common/notification_service.h" #include "grit/browser_resources.h" #include "grit/chromium_strings.h" @@ -20,124 +21,49 @@ #include "grit/locale_settings.h" #include "grit/theme_resources.h" -static const char* kTimeZonesUtf8[] = { - "Pacific/Samoa", - "US/Hawaii", - "US/Alaska", - "US/Pacific", - "US/Mountain", - "US/Central", - "US/Eastern", - "America/Santiago", - "America/Sao_Paulo", - "Atlantic/South_Georgia", - "Atlantic/Cape_Verde", - "Europe/London", - "Europe/Rome", - "Europe/Helsinki", - "Europe/Moscow", - "Asia/Dubai", - "Asia/Karachi", - "Asia/Dhaka", - "Asia/Bangkok", - "Asia/Hong_Kong", - "Asia/Tokyo", - "Australia/Sydney", - "Asia/Magadan", - "Pacific/Auckland" }; - -SystemOptionsHandler::SystemOptionsHandler() { - // TODO(chocobo): For now, add all the GMT timezones. - // We may eventually want to use icu::TimeZone::createEnumeration() - // to list all the timezones and pick the ones we want to show. - // NOTE: This currently does not handle daylight savings properly - // b/c this is just a manually selected list of timezones that - // happen to span the GMT-11 to GMT+12 Today. When daylight savings - // kick in, this list might have more than one timezone in the same - // GMT bucket. - for (size_t i = 0; i < arraysize(kTimeZonesUtf8); i++) { - timezones_.push_back(icu::TimeZone::createTimeZone( - icu::UnicodeString::fromUTF8(kTimeZonesUtf8[i]))); - } +SystemOptionsHandler::SystemOptionsHandler() + : chromeos::CrosOptionsPageUIHandler( + new chromeos::SystemSettingsProvider()) { } SystemOptionsHandler::~SystemOptionsHandler() { - STLDeleteElements(&timezones_); } void SystemOptionsHandler::GetLocalizedValues( DictionaryValue* localized_strings) { DCHECK(localized_strings); // System page - ChromeOS - localized_strings->SetString(L"systemPage", - l10n_util::GetString(IDS_OPTIONS_SYSTEM_TAB_LABEL)); - localized_strings->SetString(L"datetime_title", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME)); - localized_strings->SetString(L"timezone", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION)); + localized_strings->SetString("systemPage", + l10n_util::GetStringUTF16(IDS_OPTIONS_SYSTEM_TAB_LABEL)); + localized_strings->SetString("datetime_title", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME)); + localized_strings->SetString("timezone", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION)); - localized_strings->SetString(L"touchpad", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_TOUCHPAD)); - localized_strings->SetString(L"enable_tap_to_click", - l10n_util::GetString( + localized_strings->SetString("touchpad", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_TOUCHPAD)); + localized_strings->SetString("enable_tap_to_click", + l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_TAP_TO_CLICK_ENABLED_DESCRIPTION)); - localized_strings->SetString(L"enable_vert_edge_scroll", - l10n_util::GetString( - IDS_OPTIONS_SETTINGS_VERT_EDGE_SCROLL_ENABLED_DESCRIPTION)); - localized_strings->SetString(L"sensitivity", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SENSITIVITY_DESCRIPTION)); - localized_strings->SetString(L"speed_factor", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SPEED_FACTOR_DESCRIPTION)); - - localized_strings->SetString(L"language", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE)); - localized_strings->SetString(L"language_customize", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_CUSTOMIZE)); + localized_strings->SetString("sensitivity", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SENSITIVITY_DESCRIPTION)); - localized_strings->SetString(L"accessibility_title", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_ACCESSIBILITY)); - localized_strings->SetString(L"accessibility", - l10n_util::GetString(IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DESCRIPTION)); - - localized_strings->Set(L"timezoneList", GetTimezoneList()); -} + localized_strings->SetString("language", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE)); + localized_strings->SetString("language_customize", + l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_CUSTOMIZE)); + localized_strings->SetString("modifier_keys_customize", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_LANGUAGES_MODIFIER_KEYS_CUSTOMIZE)); -ListValue* SystemOptionsHandler::GetTimezoneList() { - ListValue* timezoneList = new ListValue(); - for (std::vector<icu::TimeZone*>::iterator iter = timezones_.begin(); - iter != timezones_.end(); ++iter) { - const icu::TimeZone* timezone = *iter; - ListValue* option = new ListValue(); - option->Append(Value::CreateStringValue(GetTimezoneID(timezone))); - option->Append(Value::CreateStringValue(GetTimezoneName(timezone))); - timezoneList->Append(option); - } - return timezoneList; -} - -std::wstring SystemOptionsHandler::GetTimezoneName( - const icu::TimeZone* timezone) { - DCHECK(timezone); - icu::UnicodeString name; - timezone->getDisplayName(name); - std::wstring output; - UTF16ToWide(name.getBuffer(), name.length(), &output); - int hour_offset = timezone->getRawOffset() / 3600000; - const wchar_t* format; - if (hour_offset == 0) - format = L"(GMT) "; - else - format = L"(GMT%+d) "; - - return StringPrintf(format, hour_offset) + output; -} + localized_strings->SetString("accessibility_title", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_SECTION_TITLE_ACCESSIBILITY)); + localized_strings->SetString("accessibility", + l10n_util::GetStringUTF16( + IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DESCRIPTION)); -std::wstring SystemOptionsHandler::GetTimezoneID( - const icu::TimeZone* timezone) { - DCHECK(timezone); - icu::UnicodeString id; - timezone->getID(id); - std::wstring output; - UTF16ToWide(id.getBuffer(), id.length(), &output); - return output; + localized_strings->Set("timezoneList", + reinterpret_cast<chromeos::SystemSettingsProvider*>( + settings_provider_.get())->GetTimezoneList()); } diff --git a/chrome/browser/chromeos/dom_ui/system_options_handler.h b/chrome/browser/chromeos/dom_ui/system_options_handler.h index c21d5b0..5d96050 100644 --- a/chrome/browser/chromeos/dom_ui/system_options_handler.h +++ b/chrome/browser/chromeos/dom_ui/system_options_handler.h @@ -4,15 +4,14 @@ #ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_SYSTEM_OPTIONS_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_DOM_UI_SYSTEM_OPTIONS_HANDLER_H_ +#pragma once -#include <string> -#include <vector> +#include "chrome/browser/chromeos/dom_ui/cros_options_page_ui_handler.h" -#include "chrome/browser/dom_ui/options_ui.h" -#include "third_party/icu/public/i18n/unicode/timezone.h" +class DictionaryValue; // ChromeOS system options page UI handler. -class SystemOptionsHandler : public OptionsPageUIHandler { +class SystemOptionsHandler : public chromeos::CrosOptionsPageUIHandler { public: SystemOptionsHandler(); virtual ~SystemOptionsHandler(); @@ -21,18 +20,6 @@ class SystemOptionsHandler : public OptionsPageUIHandler { virtual void GetLocalizedValues(DictionaryValue* localized_strings); private: - // Creates the map of timezones used by the options page. - ListValue* GetTimezoneList(); - - // Gets timezone name. - std::wstring GetTimezoneName(const icu::TimeZone* timezone); - - // Gets timezone ID which is also used as timezone pref value. - std::wstring GetTimezoneID(const icu::TimeZone* timezone); - - // Timezones. - std::vector<icu::TimeZone*> timezones_; - DISALLOW_COPY_AND_ASSIGN(SystemOptionsHandler); }; diff --git a/chrome/browser/chromeos/drop_shadow_label.h b/chrome/browser/chromeos/drop_shadow_label.h index 21ed7eb..5848160 100644 --- a/chrome/browser/chromeos/drop_shadow_label.h +++ b/chrome/browser/chromeos/drop_shadow_label.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_DROP_SHADOW_LABEL_H_ #define CHROME_BROWSER_CHROMEOS_DROP_SHADOW_LABEL_H_ +#pragma once #include "gfx/font.h" #include "views/controls/label.h" diff --git a/chrome/browser/chromeos/external_cookie_handler.h b/chrome/browser/chromeos/external_cookie_handler.h index aef3367..b422ace 100644 --- a/chrome/browser/chromeos/external_cookie_handler.h +++ b/chrome/browser/chromeos/external_cookie_handler.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_EXTERNAL_COOKIE_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_EXTERNAL_COOKIE_HANDLER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/external_metrics.h b/chrome/browser/chromeos/external_metrics.h index 0af3dad..eac8c64 100644 --- a/chrome/browser/chromeos/external_metrics.h +++ b/chrome/browser/chromeos/external_metrics.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_EXTERNAL_METRICS_H_ #define CHROME_BROWSER_CHROMEOS_EXTERNAL_METRICS_H_ +#pragma once #include "base/basictypes.h" #include "base/compiler_specific.h" diff --git a/chrome/browser/chromeos/external_protocol_dialog.cc b/chrome/browser/chromeos/external_protocol_dialog.cc index 5aeb8a9..2c53a29 100644 --- a/chrome/browser/chromeos/external_protocol_dialog.cc +++ b/chrome/browser/chromeos/external_protocol_dialog.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -82,7 +82,7 @@ views::View* ExternalProtocolDialog::GetContentsView() { ExternalProtocolDialog::ExternalProtocolDialog(TabContents* tab_contents, const GURL& url) : creation_time_(base::TimeTicks::Now()), - scheme_(UTF8ToWide(url.scheme())) { + scheme_(url.scheme()) { const int kMaxUrlWithoutSchemeSize = 256; std::wstring elided_url_without_scheme; ElideString(ASCIIToWide(url.possibly_invalid_spec()), diff --git a/chrome/browser/chromeos/external_protocol_dialog.h b/chrome/browser/chromeos/external_protocol_dialog.h index 71ca656..84f7955 100644 --- a/chrome/browser/chromeos/external_protocol_dialog.h +++ b/chrome/browser/chromeos/external_protocol_dialog.h @@ -1,9 +1,10 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_EXTERNAL_PROTOCOL_DIALOG_H_ #define CHROME_BROWSER_CHROMEOS_EXTERNAL_PROTOCOL_DIALOG_H_ +#pragma once #include "base/time.h" #include "views/window/dialog_delegate.h" @@ -43,7 +44,7 @@ class ExternalProtocolDialog : public views::DialogDelegate { base::TimeTicks creation_time_; // The scheme of the url. - std::wstring scheme_; + std::string scheme_; DISALLOW_COPY_AND_ASSIGN(ExternalProtocolDialog); }; diff --git a/chrome/browser/chromeos/frame/browser_frame_chromeos.cc b/chrome/browser/chromeos/frame/browser_frame_chromeos.cc index c0ca464..c946924 100644 --- a/chrome/browser/chromeos/frame/browser_frame_chromeos.cc +++ b/chrome/browser/chromeos/frame/browser_frame_chromeos.cc @@ -4,8 +4,11 @@ #include "chrome/browser/chromeos/frame/browser_frame_chromeos.h" -#include "chrome/browser/chromeos/frame/normal_browser_frame_view.h" +#include "base/command_line.h" #include "chrome/browser/views/frame/browser_view.h" +#include "chrome/browser/views/frame/opaque_browser_frame_view.h" +#include "chrome/browser/views/frame/popup_non_client_frame_view.h" +#include "chrome/common/chrome_switches.h" // static (Factory method.) BrowserFrame* BrowserFrame::Create(BrowserView* browser_view, @@ -27,21 +30,28 @@ BrowserFrameChromeos::~BrowserFrameChromeos() { } void BrowserFrameChromeos::Init() { - // Excludes a browser intance that requires icon/title. This is typically true - // for dev tools and javascript console. - // TODO(oshima): handle app panels. This currently uses the default - // implementation, which opens Chrome's app panel instead of - // ChromeOS's panel. - if (!IsPanel() && - !browser_view()->ShouldShowWindowIcon() && - !browser_view()->ShouldShowWindowTitle()) { - set_browser_frame_view(new NormalBrowserFrameView(this, browser_view())); + // NOTE: This logic supersedes the logic in BrowserFrameGtk::Init() + // by always setting browser_frame_view_. + if (IsPanel()) { + // ChromeOS Panels should always use PopupNonClientFrameView. + set_browser_frame_view(new PopupNonClientFrameView()); + } else { + // Default FrameView. + set_browser_frame_view(new OpaqueBrowserFrameView(this, browser_view())); } + BrowserFrameGtk::Init(); + + if (!IsPanel()) { + // On chromeos we want windows to always render as active. + GetNonClientView()->DisableInactiveRendering(true); + } } bool BrowserFrameChromeos::IsMaximized() const { - return !IsPanel() || WindowGtk::IsMaximized(); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeosFrame)) + return WindowGtk::IsMaximized(); + return !IsFullscreen() && (!IsPanel() || WindowGtk::IsMaximized()); } bool BrowserFrameChromeos::IsPanel() const { diff --git a/chrome/browser/chromeos/frame/browser_frame_chromeos.h b/chrome/browser/chromeos/frame/browser_frame_chromeos.h index b776591..4863047 100644 --- a/chrome/browser/chromeos/frame/browser_frame_chromeos.h +++ b/chrome/browser/chromeos/frame/browser_frame_chromeos.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_FRAME_CHROMEOS_H_ #define CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_FRAME_CHROMEOS_H_ +#pragma once #include "chrome/browser/views/frame/browser_frame_gtk.h" diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc index 25ecefd..8ffbe2e 100644 --- a/chrome/browser/chromeos/frame/browser_view.cc +++ b/chrome/browser/chromeos/frame/browser_view.cc @@ -12,11 +12,10 @@ #include "base/command_line.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/chromeos/frame/panel_browser_view.h" -#include "chrome/browser/chromeos/options/language_config_view.h" -#include "chrome/browser/chromeos/status/status_area_view.h" #include "chrome/browser/chromeos/status/language_menu_button.h" #include "chrome/browser/chromeos/status/network_menu_button.h" #include "chrome/browser/chromeos/status/status_area_button.h" +#include "chrome/browser/chromeos/status/status_area_view.h" #include "chrome/browser/chromeos/view_ids.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/views/app_launcher.h" @@ -28,12 +27,11 @@ #include "chrome/browser/views/theme_background.h" #include "chrome/browser/views/toolbar_view.h" #include "chrome/common/chrome_switches.h" +#include "cros/chromeos_wm_ipc_enums.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" -#include "cros/chromeos_wm_ipc_enums.h" #include "views/controls/button/button.h" #include "views/controls/button/image_button.h" -#include "views/controls/image_view.h" #include "views/controls/menu/menu_2.h" #include "views/screen.h" #include "views/widget/root_view.h" @@ -42,15 +40,6 @@ namespace { -// The OTR avatar ends 2 px above the bottom of the tabstrip (which, given the -// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the -// user). -const int kOTRBottomSpacing = 2; - -// There are 2 px on each side of the OTR avatar (between the frame border and -// it on the left, and between it and the tabstrip on the right). -const int kOTRSideSpacing = 2; - // Amount to offset the toolbar by when vertical tabs are enabled. const int kVerticalTabStripToolbarOffset = 2; @@ -83,9 +72,6 @@ class BrowserViewLayout : public ::BrowserViewLayout { case VIEW_ID_STATUS_AREA: status_area_ = static_cast<chromeos::StatusAreaView*>(view); break; - case VIEW_ID_OTR_AVATAR: - otr_avatar_icon_ = view; - break; } } @@ -95,21 +81,20 @@ class BrowserViewLayout : public ::BrowserViewLayout { virtual int LayoutTabStrip() { if (browser_view_->IsFullscreen() || !browser_view_->IsTabStripVisible()) { status_area_->SetVisible(false); - otr_avatar_icon_->SetVisible(false); tabstrip_->SetVisible(false); tabstrip_->SetBounds(0, 0, 0, 0); return 0; - } else { - gfx::Rect layout_bounds = - browser_view_->frame()->GetBoundsForTabStrip(tabstrip_); - gfx::Point tabstrip_origin = layout_bounds.origin(); - views::View::ConvertPointToView(browser_view_->GetParent(), browser_view_, - &tabstrip_origin); - layout_bounds.set_origin(tabstrip_origin); - if (browser_view_->UseVerticalTabs()) - return LayoutTitlebarComponentsWithVerticalTabs(layout_bounds); - return LayoutTitlebarComponents(layout_bounds); } + + gfx::Rect tabstrip_bounds( + browser_view_->frame()->GetBoundsForTabStrip(tabstrip_)); + gfx::Point tabstrip_origin = tabstrip_bounds.origin(); + views::View::ConvertPointToView(browser_view_->GetParent(), browser_view_, + &tabstrip_origin); + tabstrip_bounds.set_origin(tabstrip_origin); + return browser_view_->UseVerticalTabs() ? + LayoutTitlebarComponentsWithVerticalTabs(tabstrip_bounds) : + LayoutTitlebarComponents(tabstrip_bounds); } virtual int LayoutToolbar(int top) { @@ -154,33 +139,20 @@ class BrowserViewLayout : public ::BrowserViewLayout { return false; } - // Positions the titlebar, toolbar, tabstrip, tabstrip and otr icon. This is + // Positions the titlebar, toolbar and tabstrip. This is // used when side tabs are enabled. int LayoutTitlebarComponentsWithVerticalTabs(const gfx::Rect& bounds) { if (bounds.IsEmpty()) return 0; tabstrip_->SetVisible(true); - otr_avatar_icon_->SetVisible(browser_view_->ShouldShowOffTheRecordAvatar()); status_area_->SetVisible(true); gfx::Size status_size = status_area_->GetPreferredSize(); int status_height = status_size.height(); - // Layout the otr icon. int status_x = bounds.x(); - if (!otr_avatar_icon_->IsVisible()) { - otr_avatar_icon_->SetBounds(0, 0, 0, 0); - } else { - gfx::Size otr_size = otr_avatar_icon_->GetPreferredSize(); - - status_height = std::max(status_height, otr_size.height()); - int y = bounds.bottom() - status_height; - otr_avatar_icon_->SetBounds(status_x, y, otr_size.width(), status_height); - status_x += otr_size.width(); - } - - // Layout the status area after the otr icon. + // Layout the status area. status_area_->SetBounds(status_x, bounds.bottom() - status_height, status_size.width(), status_height); @@ -211,66 +183,33 @@ class BrowserViewLayout : public ::BrowserViewLayout { return bounds.y() + toolbar_height; } - // Layouts components in the title bar area (given by - // |bounds|). These include the main menu, the otr avatar icon (in - // incognito window), the tabstrip and the the status area. + // Lays out tabstrip and status area in the title bar area (given by + // |bounds|). int LayoutTitlebarComponents(const gfx::Rect& bounds) { - if (bounds.IsEmpty()) { + if (bounds.IsEmpty()) return 0; - } + tabstrip_->SetVisible(true); - otr_avatar_icon_->SetVisible(browser_view_->ShouldShowOffTheRecordAvatar()); status_area_->SetVisible(true); - int bottom = bounds.bottom(); - // Layout status area after tab strip. gfx::Size status_size = status_area_->GetPreferredSize(); - status_area_->SetBounds(bounds.x() + bounds.width() - status_size.width(), - bounds.y(), status_size.width(), - status_size.height()); - LayoutOTRAvatar(bounds); - - int curx = bounds.x(); - int remaining_width = std::max( - 0, // In case there is no space left. - otr_avatar_icon_->bounds().x() - curx); - - tabstrip_->SetBounds(curx, bounds.y(), remaining_width, bounds.height()); - - gfx::Rect toolbar_bounds = browser_view_->GetToolbarBounds(); - tabstrip_->SetBackgroundOffset( - gfx::Point(curx - toolbar_bounds.x(), bounds.y())); - return bottom; - } - - // Layouts OTR avatar within the given |bounds|. - void LayoutOTRAvatar(const gfx::Rect& bounds) { - gfx::Rect status_bounds = status_area_->bounds(); - if (!otr_avatar_icon_->IsVisible()) { - otr_avatar_icon_->SetBounds(status_bounds.x(), status_bounds.y(), 0, 0); - } else { - gfx::Size preferred_size = otr_avatar_icon_->GetPreferredSize(); - - int y = bounds.bottom() - preferred_size.height() - kOTRBottomSpacing; - int x = status_bounds.x() - kOTRSideSpacing - preferred_size.width(); - otr_avatar_icon_->SetBounds(x, y, preferred_size.width(), - preferred_size.height()); - } + status_area_->SetBounds(bounds.right() - status_size.width(), bounds.y(), + status_size.width(), status_size.height()); + tabstrip_->SetBounds(bounds.x(), bounds.y(), + std::max(0, status_area_->bounds().x() - bounds.x()), + bounds.height()); + return bounds.bottom(); } - chromeos::StatusAreaView* status_area_; - views::View* otr_avatar_icon_; DISALLOW_COPY_AND_ASSIGN(BrowserViewLayout); }; BrowserView::BrowserView(Browser* browser) : ::BrowserView(browser), - status_area_(NULL), - force_maximized_window_(false), - otr_avatar_icon_(new views::ImageView()) { + status_area_(NULL) { } BrowserView::~BrowserView() { @@ -294,10 +233,6 @@ void BrowserView::Init() { BrowserFrameGtk* gtk_frame = static_cast<BrowserFrameGtk*>(frame()); gtk_frame->GetNonClientView()->SetContextMenuController(this); - otr_avatar_icon_->SetImage(GetOTRAvatarIcon()); - otr_avatar_icon_->SetID(VIEW_ID_OTR_AVATAR); - AddChildView(otr_avatar_icon_); - // Make sure the window is set to the right type. std::vector<int> params; params.push_back(browser()->tab_count()); @@ -333,11 +268,6 @@ views::LayoutManager* BrowserView::CreateLayoutManager() const { return new BrowserViewLayout(); } -void BrowserView::InitTabStrip(TabStripModel* tab_strip_model) { - ::BrowserView::InitTabStrip(tab_strip_model); - UpdateOTRBackground(); -} - void BrowserView::ChildPreferredSizeChanged(View* child) { Layout(); SchedulePaint(); @@ -394,8 +324,7 @@ void BrowserView::OpenButtonOptions(const views::View* button_view) const { if (button_view == status_area_->network_view()) { browser()->OpenInternetOptionsDialog(); } else if (button_view == status_area_->language_view()) { - LanguageConfigView::Show(GetProfile(), - frame()->GetWindow()->GetNativeWindow()); + browser()->OpenLanguageOptionsDialog(); } else { browser()->OpenSystemOptionsDialog(); } @@ -432,20 +361,15 @@ void BrowserView::InitSystemMenu() { system_menu_menu_.reset(new views::Menu2(system_menu_contents_.get())); } -void BrowserView::UpdateOTRBackground() { - if (UseVerticalTabs()) - otr_avatar_icon_->set_background(new ThemeBackground(this)); - else - otr_avatar_icon_->set_background(NULL); -} - } // namespace chromeos // static BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) { // Create a browser view for chromeos. BrowserView* view; - if (browser->type() & Browser::TYPE_POPUP) + if ((browser->type() == Browser::TYPE_POPUP) || + (browser->type() == Browser::TYPE_APP_POPUP) || + (browser->type() == Browser::TYPE_APP_PANEL)) view = new chromeos::PanelBrowserView(browser); else view = new chromeos::BrowserView(browser); diff --git a/chrome/browser/chromeos/frame/browser_view.h b/chrome/browser/chromeos/frame/browser_view.h index 0e9f40b..3c81c12 100644 --- a/chrome/browser/chromeos/frame/browser_view.h +++ b/chrome/browser/chromeos/frame/browser_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_VIEW_H_ +#pragma once #include <vector> @@ -49,7 +50,6 @@ class BrowserView : public ::BrowserView, virtual void Show(); virtual void FocusChromeOSStatus(); virtual views::LayoutManager* CreateLayoutManager() const; - virtual void InitTabStrip(TabStripModel* tab_strip_model); virtual void ChildPreferredSizeChanged(View* child); virtual bool GetSavedWindowBounds(gfx::Rect* bounds) const; @@ -75,10 +75,6 @@ class BrowserView : public ::BrowserView, private: void InitSystemMenu(); - // Updates the background of the otr icon. The background differs for vertical - // tabs. - void UpdateOTRBackground(); - // Status Area view. StatusAreaView* status_area_; @@ -86,12 +82,6 @@ class BrowserView : public ::BrowserView, scoped_ptr<menus::SimpleMenuModel> system_menu_contents_; scoped_ptr<views::Menu2> system_menu_menu_; - // A flag to specify if the browser window should be maximized. - bool force_maximized_window_; - - // Off the record icon. - views::ImageView* otr_avatar_icon_; - DISALLOW_COPY_AND_ASSIGN(BrowserView); }; diff --git a/chrome/browser/chromeos/frame/normal_browser_frame_view.cc b/chrome/browser/chromeos/frame/normal_browser_frame_view.cc deleted file mode 100644 index 84c4614..0000000 --- a/chrome/browser/chromeos/frame/normal_browser_frame_view.cc +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright (c) 2010 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/frame/normal_browser_frame_view.h" - -#include "app/l10n_util.h" -#include "app/resource_bundle.h" -#include "app/theme_provider.h" -#include "app/x11_util.h" -#include "base/command_line.h" -#include "base/compiler_specific.h" -#include "chrome/browser/browser_theme_provider.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/views/frame/browser_frame.h" -#include "chrome/browser/views/frame/browser_view.h" -#include "chrome/browser/views/tabs/tab_strip.h" -#include "chrome/common/chrome_switches.h" -#include "gfx/canvas.h" -#include "gfx/font.h" -#include "gfx/path.h" -#include "grit/app_resources.h" -#include "grit/chromium_strings.h" -#include "grit/generated_resources.h" -#include "grit/theme_resources.h" -#include "views/controls/button/image_button.h" -#include "views/controls/image_view.h" -#include "views/window/hit_test.h" -#include "views/widget/root_view.h" -#include "views/window/window.h" -#include "views/window/window_resources.h" - -namespace { - -// The frame border is usually 0 as chromeos has no border. The border -// can be enabled (4px fixed) with the command line option -// "--chromeos-frame" so that one can resize the window on dev machine. -const int kFrameBorderThicknessForDev = 4; - -// While resize areas on Windows are normally the same size as the window -// borders, our top area is shrunk by 1 px to make it easier to move the window -// around with our thinner top grabbable strip. (Incidentally, our side and -// bottom resize areas don't match the frame border thickness either -- they -// span the whole nonclient area, so there's no "dead zone" for the mouse.) -const int kTopResizeAdjust = 1; - -// In the window corners, the resize areas don't actually expand bigger, but the -// 16 px at the end of each edge triggers diagonal resizing. -const int kResizeAreaCornerSize = 16; - -// The icon is inset 2 px from the left frame border. -const int kIconLeftSpacing = 2; - -// The top 1 px of the tabstrip is shadow; in maximized mode we push this off -// the top of the screen so the tabs appear flush against the screen edge. -const int kTabstripTopShadowThickness = 1; - -const int kCustomFrameBackgroundVerticalOffset = 15; - -} // namespace - -namespace chromeos { - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, public: - -NormalBrowserFrameView::NormalBrowserFrameView(BrowserFrame* frame, - BrowserView* browser_view) - : BrowserNonClientFrameView(), - frame_(frame), - browser_view_(browser_view) { - // Normal window does not have the window title/icon. - DCHECK(!browser_view_->ShouldShowWindowIcon()); - DCHECK(!browser_view_->ShouldShowWindowTitle()); - DCHECK(!frame_->GetWindow()->GetDelegate()->ShouldShowWindowTitle()); - DCHECK(!frame_->GetWindow()->GetDelegate()->ShouldShowWindowIcon()); -} - -NormalBrowserFrameView::~NormalBrowserFrameView() { -} - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, BrowserNonClientFrameView implementation: - -gfx::Rect NormalBrowserFrameView::GetBoundsForTabStrip( - BaseTabStrip* tabstrip) const { - int border_thickness = FrameBorderThickness(); - if (browser_view_->UseVerticalTabs()) { - // BrowserViewLayout adjusts the height/width based on the status area and - // otr icon. - gfx::Size ps = tabstrip->GetPreferredSize(); - return gfx::Rect(border_thickness, NonClientTopBorderHeight(), - ps.width(), browser_view_->height()); - } - return gfx::Rect(border_thickness, NonClientTopBorderHeight(), - std::max(0, width() - (2 * border_thickness)), - tabstrip->GetPreferredHeight()); -} - -void NormalBrowserFrameView::UpdateThrobber(bool running) { - // No window icon. -} - -gfx::Size NormalBrowserFrameView::GetMinimumSize() { - gfx::Size min_size(browser_view_->GetMinimumSize()); - int border_thickness = FrameBorderThickness(); - min_size.Enlarge(2 * border_thickness, - NonClientTopBorderHeight() + border_thickness); - - int min_titlebar_width = (2 * border_thickness) + kIconLeftSpacing; - min_size.set_width(std::max(min_size.width(), min_titlebar_width)); - return min_size; -} - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, views::NonClientFrameView implementation: - -gfx::Rect NormalBrowserFrameView::GetBoundsForClientView() const { - return client_view_bounds_; -} - -bool NormalBrowserFrameView::AlwaysUseNativeFrame() const { - return frame_->AlwaysUseNativeFrame(); -} - -gfx::Rect NormalBrowserFrameView::GetWindowBoundsForClientBounds( - const gfx::Rect& client_bounds) const { - int top_height = NonClientTopBorderHeight(); - int border_thickness = FrameBorderThickness(); - return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), - std::max(0, client_bounds.y() - top_height), - client_bounds.width() + (2 * border_thickness), - client_bounds.height() + top_height + border_thickness); -} - -int NormalBrowserFrameView::NonClientHitTest(const gfx::Point& point) { - if (!bounds().Contains(point)) - return HTNOWHERE; - int frame_component = - frame_->GetWindow()->GetClientView()->NonClientHitTest(point); - if (frame_component != HTNOWHERE) - return frame_component; - int border_thickness = FrameBorderThickness(); - int window_component = GetHTComponentForFrame(point, - std::max(0, border_thickness - kTopResizeAdjust), border_thickness, - kResizeAreaCornerSize, kResizeAreaCornerSize, - frame_->GetWindow()->GetDelegate()->CanResize()); - // Fall back to the caption if no other component matches. - return (window_component == HTNOWHERE) ? HTCAPTION : window_component; -} - -void NormalBrowserFrameView::GetWindowMask(const gfx::Size& size, - gfx::Path* window_mask) { - DCHECK(window_mask); - // Always maximized. -} - -void NormalBrowserFrameView::EnableClose(bool enable) { - // No close button -} - -void NormalBrowserFrameView::ResetWindowControls() { -} - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, views::View overrides: - -void NormalBrowserFrameView::Paint(gfx::Canvas* canvas) { - views::Window* window = frame_->GetWindow(); - if (window->IsFullscreen()) - return; // Nothing is visible, so don't bother to paint. - - PaintMaximizedFrameBorder(canvas); - PaintToolbarBackground(canvas); -} - -void NormalBrowserFrameView::Layout() { - int top_height = NonClientTopBorderHeight(); - int border_thickness = FrameBorderThickness(); - client_view_bounds_ = gfx::Rect(border_thickness, top_height, - std::max(0, width() - (2 * border_thickness)), - std::max(0, height() - top_height - border_thickness)); -} - -bool NormalBrowserFrameView::HitTest(const gfx::Point& l) const { - // If the point is outside the bounds of the client area, claim it. - if (NonClientFrameView::HitTest(l)) - return true; - - // Otherwise claim it only if it's in a non-tab portion of the tabstrip. - bool vertical_tabs = browser_view_->UseVerticalTabs(); - const gfx::Rect& tabstrip_bounds = browser_view_->tabstrip()->bounds(); - if ((!vertical_tabs && l.y() > tabstrip_bounds.bottom()) || - (vertical_tabs && (l.x() > tabstrip_bounds.right() || - l.y() > tabstrip_bounds.bottom()))) { - return false; - } - - // We convert from our parent's coordinates since we assume we fill its bounds - // completely. We need to do this since we're not a parent of the tabstrip, - // meaning ConvertPointToView would otherwise return something bogus. - gfx::Point browser_view_point(l); - View::ConvertPointToView(GetParent(), browser_view_, &browser_view_point); - return browser_view_->IsPositionInWindowCaption(browser_view_point); -} - -void NormalBrowserFrameView::ViewHierarchyChanged(bool is_add, - views::View* parent, - views::View* child) { - if (is_add && child == this) { - // The Accessibility glue looks for the product name on these two views to - // determine if this is in fact a Chrome window. - GetRootView()->SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); - } -} - -bool NormalBrowserFrameView::GetAccessibleRole(AccessibilityTypes::Role* role) { - DCHECK(role); - - *role = AccessibilityTypes::ROLE_TITLEBAR; - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, TabIconView::TabContentsProvider implementation: - -bool NormalBrowserFrameView::ShouldTabIconViewAnimate() const { - // This function is queried during the creation of the window as the - // TabIconView we host is initialized, so we need to NULL check the selected - // TabContents because in this condition there is not yet a selected tab. - TabContents* current_tab = browser_view_->GetSelectedTabContents(); - return current_tab ? current_tab->is_loading() : false; -} - -SkBitmap NormalBrowserFrameView::GetFavIconForTabIconView() { - return frame_->GetWindow()->GetDelegate()->GetWindowIcon(); -} - -/////////////////////////////////////////////////////////////////////////////// -// NormalBrowserFrameView, private: - -int NormalBrowserFrameView::FrameBorderThickness() const { - static int border_thickness_ = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeosFrame) ? - kFrameBorderThicknessForDev : 0; - return border_thickness_; -} - -int NormalBrowserFrameView::NonClientTopBorderHeight() const { - return std::max(0, FrameBorderThickness() - - (browser_view_->IsTabStripVisible() ? kTabstripTopShadowThickness : 0)); -} - -void NormalBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { - ThemeProvider* tp = GetThemeProvider(); - views::Window* window = frame_->GetWindow(); - - // Window frame mode and color - SkBitmap* theme_frame; - int y = 0; - // Never theme app and popup windows. - if (!browser_view_->IsBrowserTypeNormal()) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - theme_frame = rb.GetBitmapNamed(ShouldPaintAsActive() ? - IDR_FRAME : IDR_FRAME_INACTIVE); - } else if (!browser_view_->IsOffTheRecord()) { - theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ? - IDR_THEME_FRAME : IDR_THEME_FRAME_INACTIVE); - // TODO(oshima): gtk based CHROMEOS is using non custom frame - // mode which does this adjustment. This should be removed - // once it's fully migrated to views. -1 is due to the layout - // difference between views and gtk and will be removed. - // See http://crbug.com/28580. - y = -kCustomFrameBackgroundVerticalOffset - 1; - } else { - theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ? - IDR_THEME_FRAME_INCOGNITO: IDR_THEME_FRAME_INCOGNITO_INACTIVE); - y = -kCustomFrameBackgroundVerticalOffset - 1; - } - // Draw the theme frame. - canvas->TileImageInt(*theme_frame, 0, y, width(), theme_frame->height()); - - // Draw the theme frame overlay - if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && - browser_view_->IsBrowserTypeNormal()) { - SkBitmap* theme_overlay = tp->GetBitmapNamed(ShouldPaintAsActive() ? - IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); - canvas->DrawBitmapInt(*theme_overlay, 0, 0); - } - - if (!browser_view_->IsToolbarVisible()) { - // There's no toolbar to edge the frame border, so we need to draw a bottom - // edge. The graphic we use for this has a built in client edge, so we clip - // it off the bottom. - SkBitmap* top_center = - tp->GetBitmapNamed(IDR_APP_TOP_CENTER); - int edge_height = top_center->height() - kClientEdgeThickness; - canvas->TileImageInt(*top_center, 0, - window->GetClientView()->y() - edge_height, width(), edge_height); - } -} - -void NormalBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { - if (!browser_view_->IsToolbarVisible()) - return; - - gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds()); - if (toolbar_bounds.IsEmpty()) - return; - - ThemeProvider* tp = GetThemeProvider(); - gfx::Point toolbar_origin(toolbar_bounds.origin()); - View::ConvertPointToView(frame_->GetWindow()->GetClientView(), - this, &toolbar_origin); - toolbar_bounds.set_origin(toolbar_origin); - - // Gross hack: We split the toolbar images into two pieces, since sometimes - // (popup mode) the toolbar isn't tall enough to show the whole image. The - // split happens between the top shadow section and the bottom gradient - // section so that we never break the gradient. - int split_point = kFrameShadowThickness * 2; - int bottom_y = toolbar_bounds.y() + split_point; - SkBitmap* toolbar_left = - tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER); - int bottom_edge_height = - std::min(toolbar_left->height(), toolbar_bounds.height()) - split_point; - - SkColor theme_toolbar_color = - tp->GetColor(BrowserThemeProvider::COLOR_TOOLBAR); - canvas->FillRectInt(theme_toolbar_color, toolbar_bounds.x(), bottom_y, - toolbar_bounds.width(), bottom_edge_height); - - int strip_height = browser_view_->GetTabStripHeight(); - SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR); - - canvas->TileImageInt(*theme_toolbar, - toolbar_bounds.x() - kClientEdgeThickness, - strip_height - kFrameShadowThickness, - toolbar_bounds.x() - kClientEdgeThickness, bottom_y, - toolbar_bounds.width() + (2 * kClientEdgeThickness), - theme_toolbar->height()); - - canvas->DrawBitmapInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point, - toolbar_bounds.x() - toolbar_left->width(), toolbar_bounds.y(), - toolbar_left->width(), split_point, false); - canvas->DrawBitmapInt(*toolbar_left, 0, - toolbar_left->height() - bottom_edge_height, toolbar_left->width(), - bottom_edge_height, toolbar_bounds.x() - toolbar_left->width(), bottom_y, - toolbar_left->width(), bottom_edge_height, false); - - SkBitmap* toolbar_center = - tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER); - canvas->TileImageInt(*toolbar_center, 0, 0, toolbar_bounds.x(), - toolbar_bounds.y(), toolbar_bounds.width(), split_point); - - SkBitmap* toolbar_right = tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER); - canvas->DrawBitmapInt(*toolbar_right, 0, 0, toolbar_right->width(), - split_point, toolbar_bounds.right(), toolbar_bounds.y(), - toolbar_right->width(), split_point, false); - canvas->DrawBitmapInt(*toolbar_right, 0, - toolbar_right->height() - bottom_edge_height, toolbar_right->width(), - bottom_edge_height, toolbar_bounds.right(), bottom_y, - toolbar_right->width(), bottom_edge_height, false); - - // Draw the content/toolbar separator. - canvas->DrawLineInt(ResourceBundle::toolbar_separator_color, - toolbar_bounds.x(), toolbar_bounds.bottom() - 1, - toolbar_bounds.right() - 1, toolbar_bounds.bottom() - 1); -} - -} // namespace chromeos diff --git a/chrome/browser/chromeos/frame/normal_browser_frame_view.h b/chrome/browser/chromeos/frame/normal_browser_frame_view.h deleted file mode 100644 index 5de45dc..0000000 --- a/chrome/browser/chromeos/frame/normal_browser_frame_view.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2010 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_FRAME_NORMAL_BROWSER_FRAME_VIEW_H_ -#define CHROME_BROWSER_CHROMEOS_FRAME_NORMAL_BROWSER_FRAME_VIEW_H_ - -#include "chrome/browser/views/frame/browser_frame.h" -#include "chrome/browser/views/frame/browser_non_client_frame_view.h" -#include "chrome/browser/views/tab_icon_view.h" -#include "views/controls/button/button.h" -#include "views/window/non_client_view.h" - -class BaseTabStrip; -class BrowserView; -namespace gfx { -class Font; -} -class TabContents; -namespace views { -class ImageButton; -class ImageView; -} - -namespace chromeos { - -class NormalBrowserFrameView : public BrowserNonClientFrameView, - public TabIconView::TabIconViewModel { - public: - // Constructs a non-client view for an BrowserFrame. - NormalBrowserFrameView(BrowserFrame* frame, BrowserView* browser_view); - virtual ~NormalBrowserFrameView(); - - // Overridden from BrowserNonClientFrameView: - virtual gfx::Rect GetBoundsForTabStrip(BaseTabStrip* tabstrip) const; - virtual void UpdateThrobber(bool running); - virtual gfx::Size GetMinimumSize(); - - protected: - // Overridden from views::NonClientFrameView: - virtual gfx::Rect GetBoundsForClientView() const; - virtual bool AlwaysUseNativeFrame() const; - virtual gfx::Rect GetWindowBoundsForClientBounds( - const gfx::Rect& client_bounds) const; - virtual int NonClientHitTest(const gfx::Point& point); - virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask); - virtual void EnableClose(bool enable); - virtual void ResetWindowControls(); - - // Overridden from views::View: - virtual void Paint(gfx::Canvas* canvas); - virtual void Layout(); - virtual bool HitTest(const gfx::Point& l) const; - virtual void ViewHierarchyChanged(bool is_add, - views::View* parent, - views::View* child); - virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); - - // Overridden from TabIconView::TabIconViewModel: - virtual bool ShouldTabIconViewAnimate() const; - virtual SkBitmap GetFavIconForTabIconView(); - - private: - // Returns the thickness of the border that makes up the window frame edges. - // This does not include any client edge. - int FrameBorderThickness() const; - - // Returns the height of the entire nonclient top border, including the window - // frame, any title area, and any connected client edge. - int NonClientTopBorderHeight() const; - - // Paint various sub-components of this view. The *FrameBorder() functions - // also paint the background of the titlebar area, since the top frame border - // and titlebar background are a contiguous component. - void PaintMaximizedFrameBorder(gfx::Canvas* canvas); - void PaintToolbarBackground(gfx::Canvas* canvas); - - // The frame that hosts this view. - BrowserFrame* frame_; - - // The BrowserView hosted within this View. - BrowserView* browser_view_; - - // The bounds of the ClientView. - gfx::Rect client_view_bounds_; - - DISALLOW_COPY_AND_ASSIGN(NormalBrowserFrameView); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_FRAME_NORMAL_BROWSER_FRAME_VIEW_H_ diff --git a/chrome/browser/chromeos/frame/panel_browser_view.cc b/chrome/browser/chromeos/frame/panel_browser_view.cc index 0d72579..b52f035 100644 --- a/chrome/browser/chromeos/frame/panel_browser_view.cc +++ b/chrome/browser/chromeos/frame/panel_browser_view.cc @@ -5,7 +5,7 @@ #include "chrome/browser/chromeos/frame/panel_browser_view.h" #include "chrome/browser/chromeos/frame/panel_controller.h" -#include "third_party/cros/chromeos_wm_ipc_enums.h" +#include "cros/chromeos_wm_ipc_enums.h" #include "views/window/window.h" namespace chromeos { @@ -36,10 +36,12 @@ void PanelBrowserView::Init() { } void PanelBrowserView::Show() { - panel_controller_.reset(new PanelController(this, GetNativeHandle())); - panel_controller_->Init( - true /* focus when opened */, bounds(), creator_xid_, - WM_IPC_PANEL_USER_RESIZE_HORIZONTALLY_AND_VERTICALLY); + if (panel_controller_.get() == NULL) { + panel_controller_.reset(new PanelController(this, GetNativeHandle())); + panel_controller_->Init( + true /* focus when opened */, bounds(), creator_xid_, + WM_IPC_PANEL_USER_RESIZE_HORIZONTALLY_AND_VERTICALLY); + } BrowserView::Show(); } diff --git a/chrome/browser/chromeos/frame/panel_browser_view.h b/chrome/browser/chromeos/frame/panel_browser_view.h index c3d7ec3..337245f 100644 --- a/chrome/browser/chromeos/frame/panel_browser_view.h +++ b/chrome/browser/chromeos/frame/panel_browser_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_FRAME_PANEL_BROWSER_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_FRAME_PANEL_BROWSER_VIEW_H_ +#pragma once #include "app/x11_util.h" #include "base/scoped_ptr.h" diff --git a/chrome/browser/chromeos/frame/panel_controller.cc b/chrome/browser/chromeos/frame/panel_controller.cc index 621f63a..2f3f7eb 100644 --- a/chrome/browser/chromeos/frame/panel_controller.cc +++ b/chrome/browser/chromeos/frame/panel_controller.cc @@ -8,20 +8,25 @@ #include "app/resource_bundle.h" #include "base/logging.h" -#include "base/singleton.h" #include "base/scoped_ptr.h" +#include "base/singleton.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/common/notification_service.h" +#include "cros/chromeos_wm_ipc_enums.h" +#include "gfx/canvas_skia.h" #include "grit/app_resources.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "cros/chromeos_wm_ipc_enums.h" +#include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/effects/SkGradientShader.h" #include "views/controls/button/image_button.h" #include "views/controls/image_view.h" #include "views/controls/label.h" #include "views/event.h" +#include "views/painter.h" #include "views/view.h" #include "views/widget/widget_gtk.h" #include "views/window/window.h" @@ -39,20 +44,43 @@ static gfx::Font* inactive_font = NULL; namespace { -const int kTitleWidth = 200; -const int kTitleHeight = 20; +const int kTitleHeight = 24; const int kTitleIconSize = 16; -const int kTitleWidthPad = 2; -const int kTitleHeightPad = 1; -const int kButtonPad = 4; - -const SkColor kActiveGradientStart = 0xffebeff9; -const SkColor kActiveGradientEnd = 0xffb3c4f6; -const SkColor kInactiveGradientStart = 0xfff2f2f2; -const SkColor kInactiveGradientEnd = 0xfff2f2f2; -const SkColor kActiveColor = SK_ColorBLACK; -const SkColor kInactiveColor = 0xff333333; -const SkColor kCloseButtonColor = SK_ColorBLACK; +const int kTitleWidthPad = 4; +const int kTitleHeightPad = 4; +const int kTitleCornerRadius = 4; +const int kTitleCloseButtonPad = 6; +const SkColor kTitleActiveGradientStart = SK_ColorWHITE; +const SkColor kTitleActiveGradientEnd = 0xffe7edf1; +const SkColor kTitleActiveColor = SK_ColorBLACK; +const SkColor kTitleInactiveColor = SK_ColorBLACK; +const SkColor kTitleCloseButtonColor = SK_ColorBLACK; + +// Used to draw the background of the panel title window. +class TitleBackgroundPainter : public views::Painter { + virtual void Paint(int w, int h, gfx::Canvas* canvas) { + SkRect rect = {0, 0, w, h}; + SkPath path; + SkScalar corners[] = { + kTitleCornerRadius, kTitleCornerRadius, + kTitleCornerRadius, kTitleCornerRadius, + 0, 0, + 0, 0 + }; + path.addRoundRect(rect, corners); + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setFlags(SkPaint::kAntiAlias_Flag); + SkPoint p[2] = {{0, 0}, {0, h}}; + SkColor colors[2] = {kTitleActiveGradientStart, kTitleActiveGradientEnd}; + SkShader* s = SkGradientShader::CreateLinear( + p, colors, NULL, 2, SkShader::kClamp_TileMode, NULL); + paint.setShader(s); + // Need to unref shader, otherwise never deleted. + s->unref(); + canvas->AsCanvasSkia()->drawPath(path, paint); + } +}; static bool resources_initialized; static void InitializeResources() { @@ -62,8 +90,10 @@ static void InitializeResources() { resources_initialized = true; ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - inactive_font = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont)); - active_font = new gfx::Font(inactive_font->DeriveFont(0, gfx::Font::BOLD)); + gfx::Font base_font = rb.GetFont(ResourceBundle::BaseFont); + // Title fonts are the same for active and inactive. + inactive_font = new gfx::Font(base_font.DeriveFont(0, gfx::Font::BOLD)); + active_font = inactive_font; close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE); close_button_m = rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK); close_button_h = rb.GetBitmapNamed(IDR_TAB_CLOSE_H); @@ -92,10 +122,10 @@ void PanelController::Init(bool initial_focus, const gfx::Rect& window_bounds, XID creator_xid, WmIpcPanelUserResizeType resize_type) { - gfx::Rect title_bounds( - 0, 0, window_bounds.width(), kTitleHeight); + gfx::Rect title_bounds(0, 0, window_bounds.width(), kTitleHeight); title_window_ = new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); + title_window_->MakeTransparent(); title_window_->Init(NULL, title_bounds); gtk_widget_set_size_request(title_window_->GetNativeView(), title_bounds.width(), title_bounds.height()); @@ -285,13 +315,14 @@ void PanelController::OnCloseButtonPressed() { PanelController::TitleContentView::TitleContentView( PanelController* panel_controller) : panel_controller_(panel_controller) { + LOG(INFO) << "panel: c " << this; InitializeResources(); close_button_ = new views::ImageButton(this); close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n); close_button_->SetImage(views::CustomButton::BS_HOT, close_button_h); close_button_->SetImage(views::CustomButton::BS_PUSHED, close_button_p); close_button_->SetBackground( - kCloseButtonColor, close_button_n, close_button_m); + kTitleCloseButtonColor, close_button_n, close_button_m); AddChildView(close_button_); title_icon_ = new views::ImageView(); @@ -300,12 +331,15 @@ PanelController::TitleContentView::TitleContentView( title_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); AddChildView(title_label_); - // Default to inactive + set_background( + views::Background::CreateBackgroundPainter( + true, new TitleBackgroundPainter())); OnFocusOut(); } void PanelController::TitleContentView::Layout() { - int close_button_x = bounds().width() - (close_button_width + kButtonPad); + int close_button_x = bounds().width() - + (close_button_width + kTitleCloseButtonPad); close_button_->SetBounds( close_button_x, (bounds().height() - close_button_height) / 2, @@ -313,15 +347,15 @@ void PanelController::TitleContentView::Layout() { close_button_height); title_icon_->SetBounds( kTitleWidthPad, - kTitleHeightPad * 2, + kTitleHeightPad, kTitleIconSize, kTitleIconSize); int title_x = kTitleWidthPad * 2 + kTitleIconSize; title_label_->SetBounds( title_x, - kTitleHeightPad, - close_button_x - (title_x + kButtonPad), - bounds().height() - kTitleHeightPad); + 0, + close_button_x - (title_x + kTitleCloseButtonPad), + bounds().height()); } bool PanelController::TitleContentView::OnMousePressed( @@ -343,18 +377,14 @@ bool PanelController::TitleContentView::OnMouseDragged( } void PanelController::TitleContentView::OnFocusIn() { - set_background(views::Background::CreateVerticalGradientBackground( - kActiveGradientStart, kActiveGradientEnd)); - title_label_->SetColor(kActiveColor); + title_label_->SetColor(kTitleActiveColor); title_label_->SetFont(*active_font); Layout(); SchedulePaint(); } void PanelController::TitleContentView::OnFocusOut() { - set_background(views::Background::CreateVerticalGradientBackground( - kInactiveGradientStart, kInactiveGradientEnd)); - title_label_->SetColor(kInactiveColor); + title_label_->SetColor(kTitleInactiveColor); title_label_->SetFont(*inactive_font); Layout(); SchedulePaint(); @@ -370,4 +400,8 @@ void PanelController::TitleContentView::ButtonPressed( panel_controller_->OnCloseButtonPressed(); } +PanelController::TitleContentView::~TitleContentView() { + LOG(INFO) << "panel: delete " << this; +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/frame/panel_controller.h b/chrome/browser/chromeos/frame/panel_controller.h index 32be295..5d4920f 100644 --- a/chrome/browser/chromeos/frame/panel_controller.h +++ b/chrome/browser/chromeos/frame/panel_controller.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_FRAME_PANEL_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_FRAME_PANEL_CONTROLLER_H_ +#pragma once #include <gtk/gtk.h> #include "app/x11_util.h" +#include "cros/chromeos_wm_ipc_enums.h" #include "views/controls/button/button.h" -#include "third_party/cros/chromeos_wm_ipc_enums.h" class BrowserView; class SkBitmap; @@ -73,7 +74,7 @@ class PanelController { public views::ButtonListener { public: explicit TitleContentView(PanelController* panelController); - virtual ~TitleContentView() {} + virtual ~TitleContentView(); virtual void Layout(); virtual bool OnMousePressed(const views::MouseEvent& event); virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); diff --git a/chrome/browser/chromeos/google_update_chromeos.cc b/chrome/browser/chromeos/google_update_chromeos.cc index a7b2cc3..c1bf0ba 100644 --- a/chrome/browser/chromeos/google_update_chromeos.cc +++ b/chrome/browser/chromeos/google_update_chromeos.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/google_update.h" +#include "chrome/browser/google/google_update.h" #include "base/message_loop.h" #include "base/path_service.h" diff --git a/chrome/browser/chromeos/gview_request_interceptor.cc b/chrome/browser/chromeos/gview_request_interceptor.cc index dba4d9d..1d2b9cc 100644 --- a/chrome/browser/chromeos/gview_request_interceptor.cc +++ b/chrome/browser/chromeos/gview_request_interceptor.cc @@ -6,13 +6,14 @@ #include "base/file_path.h" #include "base/path_service.h" +#include "base/singleton.h" #include "chrome/common/chrome_paths.h" +#include "googleurl/src/gurl.h" #include "net/base/escape.h" #include "net/base/load_flags.h" -#include "net/url_request/url_request_job.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" #include "net/url_request/url_request_redirect_job.h" -#include "googleurl/src/gurl.h" #include "webkit/glue/plugins/plugin_list.h" namespace chromeos { diff --git a/chrome/browser/chromeos/gview_request_interceptor.h b/chrome/browser/chromeos/gview_request_interceptor.h index dcb5f20..adbbd25 100644 --- a/chrome/browser/chromeos/gview_request_interceptor.h +++ b/chrome/browser/chromeos/gview_request_interceptor.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_GVIEW_REQUEST_INTERCEPTOR_H__ #define CHROME_BROWSER_CHROMEOS_GVIEW_REQUEST_INTERCEPTOR_H__ +#pragma once #include <string> #include "base/hash_tables.h" diff --git a/chrome/browser/chromeos/gview_request_interceptor_unittest.cc b/chrome/browser/chromeos/gview_request_interceptor_unittest.cc index 1cc28b9..7ba3b5d 100644 --- a/chrome/browser/chromeos/gview_request_interceptor_unittest.cc +++ b/chrome/browser/chromeos/gview_request_interceptor_unittest.cc @@ -5,12 +5,15 @@ #include <string> #include "base/message_loop.h" #include "chrome/browser/chromeos/gview_request_interceptor.h" +#include "chrome/browser/plugin_service.h" +#include "chrome/common/chrome_paths.h" #include "net/base/load_flags.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_unittest.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/glue/plugins/plugin_list.h" namespace chromeos { @@ -49,6 +52,7 @@ class GViewRequestInterceptorTest : public testing::Test { URLRequest::RegisterProtocolFactory("http", &GViewRequestInterceptorTest::Factory); interceptor_ = GViewRequestInterceptor::GetGViewRequestInterceptor(); + ASSERT_TRUE(PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path_)); } virtual void TearDown() { @@ -61,10 +65,44 @@ class GViewRequestInterceptorTest : public testing::Test { return new GViewURLRequestTestJob(request); } + void RegisterPDFPlugin() { + NPAPI::PluginVersionInfo info; + info.path = pdf_path_; + memset(&info.entry_points, 0, sizeof(info.entry_points)); + NPAPI::PluginList::Singleton()->RegisterInternalPlugin(info); + NPAPI::PluginList::Singleton()->RefreshPlugins(); + } + + void UnregisterPDFPlugin() { + NPAPI::PluginList::Singleton()->UnregisterInternalPlugin(pdf_path_); + NPAPI::PluginList::Singleton()->RefreshPlugins(); + } + + void SetPDFPluginLoadedState(bool want_loaded, bool* out_is_enabled) { + WebPluginInfo info; + bool is_loaded = + NPAPI::PluginList::Singleton()->GetPluginInfoByPath(pdf_path_, &info); + if (is_loaded && !want_loaded) { + UnregisterPDFPlugin(); + is_loaded = + NPAPI::PluginList::Singleton()->GetPluginInfoByPath(pdf_path_, &info); + } else if (!is_loaded && want_loaded) { + // This "loads" the plug-in even if it's not present on the + // system - which is OK since we don't actually use it, just + // need it to be "enabled" for the test. + RegisterPDFPlugin(); + is_loaded = + NPAPI::PluginList::Singleton()->GetPluginInfoByPath(pdf_path_, &info); + } + EXPECT_EQ(want_loaded, is_loaded); + *out_is_enabled = info.enabled; + } + protected: MessageLoopForIO message_loop_; TestDelegate test_delegate_; URLRequest::Interceptor* interceptor_; + FilePath pdf_path_; }; TEST_F(GViewRequestInterceptorTest, DoNotInterceptHtml) { @@ -84,7 +122,46 @@ TEST_F(GViewRequestInterceptorTest, DoNotInterceptDownload) { EXPECT_EQ(GURL("http://foo.com/file.pdf"), request.url()); } -TEST_F(GViewRequestInterceptorTest, InterceptPdf) { +TEST_F(GViewRequestInterceptorTest, DoNotInterceptPdfWhenEnabled) { + bool enabled; + SetPDFPluginLoadedState(true, &enabled); + + if (!enabled) { + bool pdf_plugin_enabled = + NPAPI::PluginList::Singleton()->EnablePlugin(pdf_path_); + EXPECT_TRUE(pdf_plugin_enabled); + } + + URLRequest request(GURL("http://foo.com/file.pdf"), &test_delegate_); + request.Start(); + MessageLoop::current()->Run(); + EXPECT_EQ(0, test_delegate_.received_redirect_count()); + EXPECT_EQ(GURL("http://foo.com/file.pdf"), request.url()); +} + +TEST_F(GViewRequestInterceptorTest, InterceptPdfWhenDisabled) { + bool enabled; + SetPDFPluginLoadedState(true, &enabled); + + if (enabled) { + bool pdf_plugin_disabled = + NPAPI::PluginList::Singleton()->DisablePlugin(pdf_path_); + EXPECT_TRUE(pdf_plugin_disabled); + } + + URLRequest request(GURL("http://foo.com/file.pdf"), &test_delegate_); + request.Start(); + MessageLoop::current()->Run(); + EXPECT_EQ(1, test_delegate_.received_redirect_count()); + EXPECT_EQ( + GURL("http://docs.google.com/gview?url=http%3A//foo.com/file.pdf"), + request.url()); +} + +TEST_F(GViewRequestInterceptorTest, InterceptPdfWithNoPlugin) { + bool enabled; + SetPDFPluginLoadedState(false, &enabled); + URLRequest request(GURL("http://foo.com/file.pdf"), &test_delegate_); request.Start(); MessageLoop::current()->Run(); diff --git a/chrome/browser/chromeos/input_method/candidate_window.cc b/chrome/browser/chromeos/input_method/candidate_window.cc index 2e95a20..3444f53 100644 --- a/chrome/browser/chromeos/input_method/candidate_window.cc +++ b/chrome/browser/chromeos/input_method/candidate_window.cc @@ -11,23 +11,26 @@ #include <vector> #include "app/app_paths.h" +#include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/at_exit.h" #include "base/command_line.h" -#include "base/file_path.h" #include "base/logging.h" #include "base/observer_list.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library_loader.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "gfx/canvas.h" -#include "gfx/font.h" #include "cros/chromeos_cros_api.h" #include "cros/chromeos_input_method_ui.h" +#include "gfx/canvas.h" +#include "gfx/font.h" +#include "grit/app_locale_settings.h" #include "views/controls/label.h" #include "views/controls/textfield/textfield.h" #include "views/event.h" @@ -54,6 +57,11 @@ const SkColor kFooterTopColor = SkColorSetRGB(0xff, 0xff, 0xff); const SkColor kFooterBottomColor = SkColorSetRGB(0xee, 0xee, 0xee); const SkColor kShortcutColor = SkColorSetRGB(0x61, 0x61, 0x61); const SkColor kDisabledShortcutColor = SkColorSetRGB(0xcc, 0xcc, 0xcc); +const SkColor kAnnotationColor = SkColorSetRGB(0x88, 0x88, 0x88); + +// We'll use a bigger font size, so Chinese characters are more readable +// in the candidate window. +const int kFontSizeDelta = 2; // Two size bigger. // The minimum width of candidate labels in the vertical candidate // window. We use this value to prevent the candidate window from being @@ -64,6 +72,29 @@ const int kMinCandidateLabelWidth = 100; // too wide when one of candidates are long. const int kMaxCandidateLabelWidth = 500; +// VerticalCandidateLabel is used for rendering candidate text in +// the vertical candidate window. +class VerticalCandidateLabel : public views::Label { + virtual ~VerticalCandidateLabel() {} + + // Returns the preferred size, but guarantees that the width has at + // least kMinCandidateLabelWidth pixels. + virtual gfx::Size GetPreferredSize() { + gfx::Size size = Label::GetPreferredSize(); + // Hack. +2 is needed to prevent labels from getting elided like + // "abc..." in some cases. TODO(satorux): Figure out why it's + // necessary. + size.set_width(size.width() + 2); + if (size.width() < kMinCandidateLabelWidth) { + size.set_width(kMinCandidateLabelWidth); + } + if (size.width() > kMaxCandidateLabelWidth) { + size.set_width(kMaxCandidateLabelWidth); + } + return size; + } +}; + // Wraps the given view with some padding, and returns it. views::View* WrapWithPadding(views::View* view, const gfx::Insets& insets) { views::View* wrapper = new views::View; @@ -83,6 +114,193 @@ views::View* WrapWithPadding(views::View* view, const gfx::Insets& insets) { return wrapper; } +// Creates shortcut text from the given index and the orientation. +std::wstring CreateShortcutText(int index, + chromeos::InputMethodLookupTable::Orientation orientation) { + // Choose the character used for the shortcut label. + const wchar_t kShortcutCharacters[] = L"1234567890ABCDEF"; + // The default character should not be used but just in case. + wchar_t shortcut_character = L'?'; + // -1 to exclude the null character at the end. + if (index < static_cast<int>(arraysize(kShortcutCharacters) - 1)) { + shortcut_character = kShortcutCharacters[index]; + } + + std::wstring shortcut_text; + if (orientation == chromeos::InputMethodLookupTable::kVertical) { + shortcut_text = base::StringPrintf(L"%lc", shortcut_character); + } else { + shortcut_text = base::StringPrintf(L"%lc.", shortcut_character); + } + + return shortcut_text; +} + +// Creates the shortcut label, and returns it (never returns NULL). +// The label text is not set in this function. +views::Label* CreateShortcutLabel( + chromeos::InputMethodLookupTable::Orientation orientation) { + // Create the shortcut label. The label will be owned by + // |wrapped_shortcut_label|, hence it's deleted when + // |wrapped_shortcut_label| is deleted. + views::Label* shortcut_label = new views::Label; + + if (orientation == chromeos::InputMethodLookupTable::kVertical) { + shortcut_label->SetFont( + shortcut_label->font().DeriveFont(kFontSizeDelta, gfx::Font::BOLD)); + } else { + shortcut_label->SetFont( + shortcut_label->font().DeriveFont(kFontSizeDelta)); + } + // TODO(satorux): Maybe we need to use language specific fonts for + // candidate_label, like Chinese font for Chinese input method? + shortcut_label->SetColor(kShortcutColor); + + return shortcut_label; +} + +// Wraps the shortcut label, then decorates wrapped shortcut label +// and returns it (never returns NULL). +// The label text is not set in this function. +views::View* CreateWrappedShortcutLabel(views::Label* shortcut_label, + chromeos::InputMethodLookupTable::Orientation orientation) { + // Wrap it with padding. + const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6); + const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0); + const gfx::Insets insets = + (orientation == chromeos::InputMethodLookupTable::kVertical ? + kVerticalShortcutLabelInsets : + kHorizontalShortcutLabelInsets); + views::View* wrapped_shortcut_label = + WrapWithPadding(shortcut_label, insets); + + // Add decoration based on the orientation. + if (orientation == chromeos::InputMethodLookupTable::kVertical) { + // Set the background color. + wrapped_shortcut_label->set_background( + views::Background::CreateSolidBackground( + kShortcutBackgroundColor)); + } + + return wrapped_shortcut_label; +} + +// Creates the candidate label, and returns it (never returns NULL). +// The label text is not set in this function. +views::Label* CreateCandidateLabel( + chromeos::InputMethodLookupTable::Orientation orientation) { + views::Label* candidate_label = NULL; + + // Create the candidate label. The label will be added to |this| as a + // child view, hence it's deleted when |this| is deleted. + if (orientation == chromeos::InputMethodLookupTable::kVertical) { + candidate_label = new VerticalCandidateLabel; + } else { + candidate_label = new views::Label; + } + + // Change the font size. + candidate_label->SetFont( + candidate_label->font().DeriveFont(kFontSizeDelta)); + candidate_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + + return candidate_label; +} + +// Creates the annotation label, and return it (never returns NULL). +// The label text is not set in this function. +views::Label* CreateAnnotationLabel( + chromeos::InputMethodLookupTable::Orientation orientation) { + // Create the annotation label. + views::Label* annotation_label = new views::Label; + + // Change the font size and color. + annotation_label->SetFont( + annotation_label->font().DeriveFont(kFontSizeDelta)); + annotation_label->SetColor(kAnnotationColor); + annotation_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + + return annotation_label; +} + +// Computes shortcut column width. +int ComputeShortcutColumnWidth( + const chromeos::InputMethodLookupTable& lookup_table) { + int shortcut_column_width = 0; + // Create the shortcut label. The label will be owned by + // |wrapped_shortcut_label|, hence it's deleted when + // |wrapped_shortcut_label| is deleted. + views::Label* shortcut_label = CreateShortcutLabel(lookup_table.orientation); + scoped_ptr<views::View> wrapped_shortcut_label( + CreateWrappedShortcutLabel(shortcut_label, lookup_table.orientation)); + + // Compute the max width in shortcut labels. + // We'll create temporary shortcut labels, and choose the largest width. + for (int i = 0; i < lookup_table.page_size; ++i) { + shortcut_label->SetText( + CreateShortcutText(i, lookup_table.orientation)); + shortcut_column_width = + std::max(shortcut_column_width, + wrapped_shortcut_label->GetPreferredSize().width()); + } + + return shortcut_column_width; +} + +// Computes candidate column width. +int ComputeCandidateColumnWidth( + const chromeos::InputMethodLookupTable& lookup_table) { + int candidate_column_width = 0; + scoped_ptr<views::Label> candidate_label( + CreateCandidateLabel(lookup_table.orientation)); + + // Compute the start index of |lookup_table_|. + const int current_page_index = + lookup_table.cursor_absolute_index / lookup_table.page_size; + const size_t start_from = current_page_index * lookup_table.page_size; + + // Compute the max width in candidate labels. + // We'll create temporary candidate labels, and choose the largest width. + for (size_t i = 0; i < lookup_table.candidates.size(); ++i) { + const size_t index = start_from + i; + + candidate_label->SetText( + UTF8ToWide(lookup_table.candidates[index])); + candidate_column_width = + std::max(candidate_column_width, + candidate_label->GetPreferredSize().width()); + } + + return candidate_column_width; +} + +// Computes annotation column width. +int ComputeAnnotationColumnWidth( + const chromeos::InputMethodLookupTable& lookup_table) { + int annotation_column_width = 0; + scoped_ptr<views::Label> annotation_label( + CreateAnnotationLabel(lookup_table.orientation)); + + // Compute the start index of |lookup_table_|. + const int current_page_index = + lookup_table.cursor_absolute_index / lookup_table.page_size; + const size_t start_from = current_page_index * lookup_table.page_size; + + // Compute max width in annotation labels. + // We'll create temporary annotation labels, and choose the largest width. + for (size_t i = 0; i < lookup_table.annotations.size(); ++i) { + const size_t index = start_from + i; + + annotation_label->SetText( + UTF8ToWide(lookup_table.annotations[index])); + annotation_column_width = + std::max(annotation_column_width, + annotation_label->GetPreferredSize().width()); + } + + return annotation_column_width; +} + } // namespace namespace chromeos { @@ -163,8 +381,7 @@ class CandidateWindowView : public views::View { private: // Initializes the candidate views if needed. void MaybeInitializeCandidateViews( - int num_views, - InputMethodLookupTable::Orientation orientation); + const InputMethodLookupTable& lookup_table); // Creates the footer area, where we show status information. // For instance, we show a cursor position like 2/19. @@ -216,6 +433,11 @@ class CandidateWindowView : public views::View { views::Label* header_label_; // The footer label is shown in the footer area. views::Label* footer_label_; + + // Current columns width in |candidate_area_|. + int previous_shortcut_column_width_; + int previous_candidate_column_width_; + int previous_annotation_column_width_; }; // CandidateRow renderes a row of a candidate. @@ -225,7 +447,11 @@ class CandidateView : public views::View { int index_in_page, InputMethodLookupTable::Orientation orientation); virtual ~CandidateView() {} - void Init(); + // Initializes the candidate view with the given column widths. + // A width of 0 means that the column is resizable. + void Init(int shortcut_column_width, + int candidate_column_width, + int annotation_column_width); // Sets candidate text to the given text. void SetCandidateText(const std::wstring& text); @@ -233,8 +459,8 @@ class CandidateView : public views::View { // Sets shortcut text to the given text. void SetShortcutText(const std::wstring& text); - // Sets shortcut text from the given integer. - void SetShortcutTextFromInt(int index); + // Sets annotation text to the given text. + void SetAnnotationText(const std::wstring& text); // Selects the candidate row. Changes the appearance to make it look // like a selected candidate. @@ -278,29 +504,8 @@ class CandidateView : public views::View { views::Label* shortcut_label_; // The candidate label renders candidates. views::Label* candidate_label_; -}; - -// VerticalCandidateLabel is used for rendering candidate text in -// the vertical candidate window. -class VerticalCandidateLabel : public views::Label { - virtual ~VerticalCandidateLabel() {} - - // Returns the preferred size, but guarantees that the width has at - // least kMinCandidateLabelWidth pixels. - virtual gfx::Size GetPreferredSize() { - gfx::Size size = Label::GetPreferredSize(); - // Hack. +2 is needed to prevent labels from getting elided like - // "abc..." in some cases. TODO(satorux): Figure out why it's - // necessary. - size.set_width(size.width() + 2); - if (size.width() < kMinCandidateLabelWidth) { - size.set_width(kMinCandidateLabelWidth); - } - if (size.width() > kMaxCandidateLabelWidth) { - size.set_width(kMaxCandidateLabelWidth); - } - return size; - } + // The annotation label renders annotations. + views::Label* annotation_label_; }; // CandidateWindowController controls the CandidateWindow. @@ -308,12 +513,12 @@ class CandidateWindowController : public CandidateWindowView::Observer { public: CandidateWindowController(); virtual ~CandidateWindowController(); - void Init(); + bool Init(); // Returns the work area of the monitor nearest the candidate window. gfx::Rect GetMonitorWorkAreaNearestWindow(); - // Moves the candidate window per the the given cursor location, and the + // Moves the candidate window per the given cursor location, and the // horizontal offset. void MoveCandidateWindow(const gfx::Rect& cursor_location, int horizontal_offset); @@ -359,6 +564,10 @@ class CandidateWindowController : public CandidateWindowView::Observer { static void OnUpdateLookupTable(void* input_method_library, const InputMethodLookupTable& lookup_table); + // This function is called by libcros when ibus connects or disconnects. + // |input_method_library| is a void pointer to this object. + static void OnConnectionChange(void* input_method_library, bool connected); + // The connection is used for communicating with input method UI logic // in libcros. InputMethodUiStatusConnection* ui_status_connection_; @@ -382,83 +591,57 @@ CandidateView::CandidateView( orientation_(orientation), parent_candidate_window_(parent_candidate_window), shortcut_label_(NULL), - candidate_label_(NULL) { + candidate_label_(NULL), + annotation_label_(NULL) { } -void CandidateView::Init() { +void CandidateView::Init(int shortcut_column_width, + int candidate_column_width, + int annotation_column_width) { views::GridLayout* layout = new views::GridLayout(this); SetLayoutManager(layout); // |this| owns |layout|. - // Create the shortcut label. The label will eventually be part of the - // tree of |this| via |wrapped_shortcut_label|, hence it's deleted when - // |this| is deleted. - shortcut_label_ = new views::Label(); - - // Wrap it with padding. - const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6); - const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0); - const gfx::Insets insets = - (orientation_ == InputMethodLookupTable::kVertical ? - kVerticalShortcutLabelInsets : - kHorizontalShortcutLabelInsets); + // Create Labels. + shortcut_label_ = CreateShortcutLabel(orientation_); views::View* wrapped_shortcut_label = - WrapWithPadding(shortcut_label_, insets); - // We'll use a bigger font size, so Chinese characters are more readable - // in the candidate window. - const int kFontSizeDelta = 2; // Two size bigger. - // Make the font bold, and change the size. - if (orientation_ == InputMethodLookupTable::kVertical) { - shortcut_label_->SetFont( - shortcut_label_->font().DeriveFont(kFontSizeDelta, gfx::Font::BOLD)); - } else { - shortcut_label_->SetFont( - shortcut_label_->font().DeriveFont(kFontSizeDelta)); - } - // TODO(satorux): Maybe we need to use language specific fonts for - // candidate_label, like Chinese font for Chinese input method? + CreateWrappedShortcutLabel(shortcut_label_, orientation_); + candidate_label_ = CreateCandidateLabel(orientation_); + annotation_label_ = CreateAnnotationLabel(orientation_); - // Add decoration based on the orientation. - if (orientation_ == InputMethodLookupTable::kVertical) { - // Set the background color. - wrapped_shortcut_label->set_background( - views::Background::CreateSolidBackground( - kShortcutBackgroundColor)); - } - shortcut_label_->SetColor(kShortcutColor); + // Initialize the column set with three columns. + views::ColumnSet* column_set = layout->AddColumnSet(0); - // Create the candidate label. The label will be added to |this| as a - // child view, hence it's deleted when |this| is deleted. - if (orientation_ == InputMethodLookupTable::kVertical) { - candidate_label_ = new VerticalCandidateLabel; - } else { - candidate_label_ = new views::Label; - } - // Change the font size. - candidate_label_->SetFont( - candidate_label_->font().DeriveFont(kFontSizeDelta)); + // If orientation is vertical, each column width is fixed. + // Otherwise the width is resizable. + const views::GridLayout::SizeType column_type = + orientation_ == InputMethodLookupTable::kVertical ? + views::GridLayout::FIXED : views::GridLayout::USE_PREF; - candidate_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + const int padding_column_width = + orientation_ == InputMethodLookupTable::kVertical ? 4 : 6; - // Initialize the column set with two columns. - views::ColumnSet* column_set = layout->AddColumnSet(0); + // Set shortcut column type and width. column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, - 0, views::GridLayout::USE_PREF, 0, 0); - if (orientation_ == InputMethodLookupTable::kVertical) { - column_set->AddPaddingColumn(0, 4); - } + 0, column_type, shortcut_column_width, 0); + column_set->AddPaddingColumn(0, padding_column_width); + + // Set candidate column type and width. column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, - 0, views::GridLayout::USE_PREF, 0, 0); - if (orientation_ == InputMethodLookupTable::kVertical) { - column_set->AddPaddingColumn(0, 4); - } else { - column_set->AddPaddingColumn(0, 6); - } + 0, column_type, candidate_column_width, 0); + column_set->AddPaddingColumn(0, padding_column_width); - // Add the shortcut label and the candidate label. + // Set annotation column type and width. + column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, + 0, column_type, annotation_column_width, 0); + column_set->AddPaddingColumn(0, padding_column_width); + + // Add the shortcut label, the candidate label, and annotation label. layout->StartRow(0, 0); - // |wrapped_shortcut_label| and |candidate_label_| will be owned by |this|. + // |wrapped_shortcut_label|, |candidate_label_|, and |annotation_label_| + // will be owned by |this|. layout->AddView(wrapped_shortcut_label); layout->AddView(candidate_label_); + layout->AddView(annotation_label_); } void CandidateView::SetCandidateText(const std::wstring& text) { @@ -469,19 +652,8 @@ void CandidateView::SetShortcutText(const std::wstring& text) { shortcut_label_->SetText(text); } -void CandidateView::SetShortcutTextFromInt(int index) { - // Choose the character used for the shortcut label. - const wchar_t kShortcutCharacters[] = L"1234567890ABCDEF"; - // The default character should not be used but just in case. - wchar_t shortcut_character = L'?'; - if (index < static_cast<int>(arraysize(kShortcutCharacters) - 1)) { - shortcut_character = kShortcutCharacters[index]; - } - if (orientation_ == InputMethodLookupTable::kVertical) { - shortcut_label_->SetText(StringPrintf(L"%lc", shortcut_character)); - } else { - shortcut_label_->SetText(StringPrintf(L"%lc.", shortcut_character)); - } +void CandidateView::SetAnnotationText(const std::wstring& text) { + annotation_label_->SetText(text); } void CandidateView::Select() { @@ -539,7 +711,10 @@ CandidateWindowView::CandidateWindowView( footer_area_(NULL), header_area_(NULL), header_label_(NULL), - footer_label_(NULL) { + footer_label_(NULL), + previous_shortcut_column_width_(0), + previous_candidate_column_width_(0), + previous_annotation_column_width_(0) { } void CandidateWindowView::Init() { @@ -613,8 +788,11 @@ void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) { void CandidateWindowView::UpdateCandidates( const InputMethodLookupTable& lookup_table) { // Initialize candidate views if necessary. - MaybeInitializeCandidateViews(lookup_table.page_size, - lookup_table.orientation); + MaybeInitializeCandidateViews(lookup_table); + + // In MaybeInitializeCandidateViews(), + // |lookup_table| values and |lookup_table_| values are compared, + // so this substitution is needed after the function. lookup_table_ = lookup_table; // Compute the index of the current page. @@ -642,16 +820,21 @@ void CandidateWindowView::UpdateCandidates( // (ex. show 6, 7, 8, ... in empty rows when the number of // candidates is 5). Second, we want to add a period after each // shortcut label when the candidate window is horizontal. - candidate_view->SetShortcutTextFromInt(i); + candidate_view->SetShortcutText( + CreateShortcutText(i, lookup_table_.orientation)); } // Set the candidate text. - if (candidate_index < lookup_table_.candidates.size()) { + if (candidate_index < lookup_table_.candidates.size() && + candidate_index < lookup_table_.annotations.size()) { candidate_view->SetCandidateText( UTF8ToWide(lookup_table_.candidates[candidate_index])); + candidate_view->SetAnnotationText( + UTF8ToWide(lookup_table_.annotations[candidate_index])); candidate_view->SetRowEnabled(true); } else { // Disable the empty row. candidate_view->SetCandidateText(L""); + candidate_view->SetAnnotationText(L""); candidate_view->SetRowEnabled(false); } } @@ -663,15 +846,39 @@ void CandidateWindowView::UpdateCandidates( } void CandidateWindowView::MaybeInitializeCandidateViews( - int num_views, - InputMethodLookupTable::Orientation orientation) { - // If the requested number of views matches the number of current views, - // just reuse these. - if (num_views == static_cast<int>(candidate_views_.size()) && - orientation == lookup_table_.orientation) { + const InputMethodLookupTable& lookup_table) { + const InputMethodLookupTable::Orientation orientation = + lookup_table.orientation; + const int page_size = lookup_table.page_size; + + // Current column width. + int shortcut_column_width = 0; + int candidate_column_width = 0; + int annotation_column_width = 0; + + // If orientation is horizontal, don't need to compute width, + // because each label is left aligned. + if (orientation == InputMethodLookupTable::kVertical) { + shortcut_column_width = ComputeShortcutColumnWidth(lookup_table); + candidate_column_width = ComputeCandidateColumnWidth(lookup_table); + annotation_column_width = ComputeAnnotationColumnWidth(lookup_table); + } + + // If the requested number of views matches the number of current views, and + // previous and current column width are same, just reuse these. + if (static_cast<int>(candidate_views_.size()) == page_size && + lookup_table_.orientation == orientation && + previous_shortcut_column_width_ == shortcut_column_width && + previous_candidate_column_width_ == candidate_column_width && + previous_annotation_column_width_ == annotation_column_width) { return; } + // Update the previous column widths. + previous_shortcut_column_width_ = shortcut_column_width; + previous_candidate_column_width_ = candidate_column_width; + previous_annotation_column_width_ = annotation_column_width; + // Clear the existing candidate_views if any. for (size_t i = 0; i < candidate_views_.size(); ++i) { candidate_area_->RemoveChildView(candidate_views_[i]); @@ -688,7 +895,7 @@ void CandidateWindowView::MaybeInitializeCandidateViews( views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); } else { - for (int i = 0; i < num_views; ++i) { + for (int i = 0; i < page_size; ++i) { column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); @@ -709,16 +916,27 @@ void CandidateWindowView::MaybeInitializeCandidateViews( if (orientation == InputMethodLookupTable::kHorizontal) { layout->StartRow(0, 0); } - for (int i = 0; i < num_views; ++i) { + + for (int i = 0; i < page_size; ++i) { CandidateView* candidate_row = new CandidateView(this, i, orientation); - candidate_row->Init(); + candidate_row->Init(shortcut_column_width, + candidate_column_width, + annotation_column_width); candidate_views_.push_back(candidate_row); if (orientation == InputMethodLookupTable::kVertical) { layout->StartRow(0, 0); } - // |candidate_row| will be owned by candidate_area_|. + // |candidate_row| will be owned by |candidate_area_|. layout->AddView(candidate_row); } + + // Compute views size in |layout|. + // If we don't call this function, GetHorizontalOffset() often + // returns invalid value (returns 0), then candidate window + // moves right from the correct position in MoveCandidateWindow(). + // TODO(nhiroki): Figure out why it returns invalid value. + // It seems that the x-position of the candidate labels is not set. + layout->Layout(this); } views::View* CandidateWindowView::CreateHeaderArea() { @@ -858,7 +1076,7 @@ int CandidateWindowView::GetHorizontalOffset() { } -void CandidateWindowController::Init() { +bool CandidateWindowController::Init() { // Initialize the input method UI status connection. InputMethodUiStatusMonitorFunctions functions; functions.hide_auxiliary_text = @@ -872,11 +1090,17 @@ void CandidateWindowController::Init() { functions.update_lookup_table = &CandidateWindowController::OnUpdateLookupTable; ui_status_connection_ = MonitorInputMethodUiStatus(functions, this); - CHECK(ui_status_connection_) - << "MonitorInputMethodUiStatus() failed."; + if (!ui_status_connection_) { + LOG(ERROR) << "MonitorInputMethodUiStatus() failed."; + return false; + } + MonitorInputMethodConnection(ui_status_connection_, + &CandidateWindowController::OnConnectionChange); // Create the candidate window view. CreateView(); + + return true; } void CandidateWindowController::CreateView() { @@ -978,6 +1202,16 @@ void CandidateWindowController::OnSetCursorLocation( CandidateWindowController* controller = static_cast<CandidateWindowController*>(input_method_library); + // A workaround for http://crosbug.com/6460. We should ignore very short Y + // move to prevent the window from shaking up and down. + const int kKeepPositionThreshold = 2; // px + const gfx::Rect& last_location = controller->cursor_location(); + const int delta_y = abs(last_location.y() - y); + if ((last_location.x() == x) && (delta_y <= kKeepPositionThreshold)) { + DLOG(INFO) << "Ignored set_cursor_location signal to prevent window shake"; + return; + } + // Remember the cursor location. controller->set_cursor_location(gfx::Rect(x, y, width, height)); // Move the window per the cursor location. @@ -1032,6 +1266,15 @@ void CandidateWindowController::OnCandidateCommitted(int index, NotifyCandidateClicked(ui_status_connection_, index, button, flags); } +void CandidateWindowController::OnConnectionChange( + void* input_method_library, + bool connected) { + if (!connected) { + MessageLoopForUI::current()->PostTask(FROM_HERE, + new MessageLoop::QuitTask()); + } +} + } // namespace chromeos int main(int argc, char** argv) { @@ -1045,30 +1288,26 @@ int main(int argc, char** argv) { base::EnableTerminationOnHeapCorruption(); app::RegisterPathProvider(); CommandLine::Init(argc, argv); - // TODO(markusheintz): The command line switch --Lang is now processed - // by the CommandLinePrefStore and mapped to the preference - // prefs::kApplicationLocale. This preferences can be read through the - // PrefService. l10n_util::GetApplicationLocale() which is called by the - // ResourceBundle code now ignores the --Lang flag. - // In order to support the --Lang flag here the preference - // prefs::kApplicationLocale must be read and passed instead of L"en-US". - ResourceBundle::InitSharedInstance(L"en-US"); - - // Write logs to a file for debugging, if --logtofile=FILE_NAME is given. + + // Check if the UI language code is passed from the command line, + // otherwise, default to "en-US". const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - std::string log_file_name = - command_line.GetSwitchValueASCII(switches::kChromeosLogToFile); - if (!log_file_name.empty()) { - logging::SetMinLogLevel(logging::LOG_INFO); - logging::InitLogging(log_file_name.c_str(), - logging::LOG_ONLY_TO_FILE, - logging::DONT_LOCK_LOG_FILE, - logging::DELETE_OLD_LOG_FILE); - // Redirect stderr to log_file_name. This is neeed to capture the - // logging from libcros.so. - if (!freopen(log_file_name.c_str(), "a", stderr)) { - LOG(INFO) << "Failed to redirect stderr to " << log_file_name.c_str(); - } + std::string ui_language_code = + command_line.GetSwitchValueASCII(switches::kCandidateWindowLang); + if (ui_language_code.empty()) { + ui_language_code = "en-US"; + } + ResourceBundle::InitSharedInstance(ui_language_code); + + // Change the UI font if needed. + const std::string font_name = + l10n_util::GetStringUTF8(IDS_UI_FONT_FAMILY_CROS); + // The font name should not be empty here, but just in case. + if (font_name != "default" && !font_name.empty()) { + // Don't use gtk_util::SetGtkFont() in chrome/browser/gtk not to + // introduce a dependency to it. + g_object_set(gtk_settings_get_default(), + "gtk-font-name", font_name.c_str(), NULL); } // Load libcros. @@ -1083,7 +1322,9 @@ int main(int argc, char** argv) { // Create the candidate window controller. chromeos::CandidateWindowController controller; - controller.Init(); + if (!controller.Init()) { + return 1; + } // Start the main loop. views::AcceleratorHandler accelerator_handler; diff --git a/chrome/browser/chromeos/input_method/candidate_window.gyp b/chrome/browser/chromeos/input_method/candidate_window.gyp index e2f3ced..0d3b446 100644 --- a/chrome/browser/chromeos/input_method/candidate_window.gyp +++ b/chrome/browser/chromeos/input_method/candidate_window.gyp @@ -11,6 +11,7 @@ 'target_name': 'candidate_window', 'type': 'executable', 'dependencies': [ + '../../../../app/app.gyp:app_strings', '../../../../base/base.gyp:base', '../../../../build/linux/system.gyp:gtk', '../../../../build/linux/system.gyp:x11', diff --git a/chrome/browser/chromeos/input_method/input_method_util.cc b/chrome/browser/chromeos/input_method/input_method_util.cc index abb94c2..3d981f1 100644 --- a/chrome/browser/chromeos/input_method/input_method_util.cc +++ b/chrome/browser/chromeos/input_method/input_method_util.cc @@ -8,12 +8,15 @@ #include <map> #include <utility> +#include "unicode/uloc.h" + #include "app/l10n_util.h" #include "app/l10n_util_collator.h" #include "base/basictypes.h" #include "base/hash_tables.h" #include "base/scoped_ptr.h" #include "base/singleton.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" @@ -22,21 +25,17 @@ #include "chrome/browser/chromeos/cros/keyboard_library.h" #include "chrome/browser/chromeos/language_preferences.h" #include "grit/generated_resources.h" -#include "third_party/icu/public/common/unicode/uloc.h" namespace { // Map from language code to associated input method IDs, etc. typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap; struct IdMaps { - LanguageCodeToIdsMap* language_code_to_ids; - std::map<std::string, std::string>* id_to_language_code; - std::map<std::string, std::string>* id_to_display_name; + scoped_ptr<LanguageCodeToIdsMap> language_code_to_ids; + scoped_ptr<std::map<std::string, std::string> > id_to_language_code; + scoped_ptr<std::map<std::string, std::string> > id_to_display_name; - private: - IdMaps() : language_code_to_ids(NULL), - id_to_language_code(NULL), - id_to_display_name(NULL) { + void ReloadMaps() { chromeos::InputMethodLibrary* library = chromeos::CrosLibrary::Get()->GetInputMethodLibrary(); scoped_ptr<chromeos::InputMethodDescriptors> supported_input_methods( @@ -46,9 +45,9 @@ struct IdMaps { // TODO(yusukes): Handle this error in nicer way. } - language_code_to_ids = new LanguageCodeToIdsMap; - id_to_language_code = new std::map<std::string, std::string>; - id_to_display_name = new std::map<std::string, std::string>; + language_code_to_ids->clear(); + id_to_language_code->clear(); + id_to_display_name->clear(); // Build the id to descriptor map for handling kExtraLanguages later. typedef std::map<std::string, @@ -81,6 +80,13 @@ struct IdMaps { } } + private: + IdMaps() : language_code_to_ids(new LanguageCodeToIdsMap), + id_to_language_code(new std::map<std::string, std::string>), + id_to_display_name(new std::map<std::string, std::string>) { + ReloadMaps(); + } + void AddInputMethodToMaps( const std::string& language_code, const chromeos::InputMethodDescriptor& input_method) { @@ -153,10 +159,6 @@ const struct EnglishToResouceId { IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD }, { "vni (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD }, - { "latn-post (m17n)", - IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_LATIN_POST_INPUT_METHOD }, - { "latn-pre (m17n)", - IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_LATIN_PRE_INPUT_METHOD }, { "Bopomofo", IDS_OPTIONS_SETTINGS_LANGUAGES_BOPOMOFO_INPUT_METHOD }, { "Chewing", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD }, { "Pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD }, @@ -190,15 +192,20 @@ const struct EnglishToResouceId { { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK }, { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA }, { "USA", IDS_STATUSBAR_LAYOUT_USA }, + { "USA - International (AltGr dead keys)", + IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL }, { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA }, { "United Kingdom - Extended - Winkeys", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM }, { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA }, { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA }, + { "Russia - Phonetic", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC }, { "Greece", IDS_STATUSBAR_LAYOUT_GREECE }, { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM }, { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA }, + { "Bulgaria - Traditional phonetic", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC }, { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND }, + { "Switzerland - French", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH }, { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY }, { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL }, { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN }, @@ -211,6 +218,11 @@ const struct EnglishToResouceId { { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN }, { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS }, { "Latvia", IDS_STATUSBAR_LAYOUT_LATVIA }, + { "Canada", IDS_STATUSBAR_LAYOUT_CANADA }, + { "Canada - English", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH }, + { "Israel", IDS_STATUSBAR_LAYOUT_ISRAEL }, + { "Korea, Republic of - 101/104 key Compatible", + IDS_STATUSBAR_LAYOUT_KOREA_104 }, }; const size_t kNumEntries = arraysize(kEnglishToResourceIdArray); @@ -231,20 +243,6 @@ const char* kIso639VariantMapping[][2] = { {"slo", "slk"}, }; -// The list defines pairs of language code and the default input method -// id. The list is used for reordering input method ids. -// -// TODO(satorux): We may need to handle secondary, and ternary input -// methods, rather than handling the default input method only. -const struct LanguageDefaultInputMethodId { - const char* language_code; - const char* input_method_id; -} kLanguageDefaultInputMethodIds[] = { - { "en-US", "xkb:us::eng", }, // US - English - { "fr", "xkb:fr::fra", }, // France - French - { "de", "xkb:de::ger", }, // Germany - German -}; - // The comparator is used for sorting language codes by their // corresponding language names, using the ICU collator. struct CompareLanguageCodesByLanguageName @@ -264,6 +262,7 @@ struct CompareLanguageCodesByLanguageName return l10n_util::StringComparator<std::wstring>(collator_)(key1, key2); } + private: icu::Collator* collator_; }; @@ -293,6 +292,7 @@ struct CompareInputMethodIdsByLanguageName return comparator_(language_code_1, language_code_2); } + private: const CompareLanguageCodesByLanguageName comparator_; const std::map<std::string, std::string>& id_to_language_code_map_; }; @@ -444,16 +444,6 @@ std::string GetLanguageCodeFromDescriptor( return language_code; } -std::wstring MaybeRewriteLanguageName(const std::wstring& language_name) { - // "t" is used as the language code for input methods that don't fall - // under any other languages. - if (language_name == L"t") { - return l10n_util::GetString( - IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS); - } - return language_name; -} - std::string GetLanguageCodeFromInputMethodId( const std::string& input_method_id) { // The code should be compatible with one of codes used for UI languages, @@ -490,10 +480,14 @@ std::wstring GetLanguageDisplayNameFromCode(const std::string& language_code) { if (!g_browser_process) { return L""; } - return MaybeRewriteLanguageName(UTF16ToWide( - l10n_util::GetDisplayNameForLocale( - language_code, g_browser_process->GetApplicationLocale(), - true))); + return UTF16ToWide(l10n_util::GetDisplayNameForLocale( + language_code, g_browser_process->GetApplicationLocale(), true)); +} + +std::wstring GetLanguageNativeDisplayNameFromCode( + const std::string& language_code) { + return UTF16ToWide(l10n_util::GetDisplayNameForLocale( + language_code, language_code, true)); } void SortLanguageCodesByNames(std::vector<std::string>* language_codes) { @@ -536,24 +530,6 @@ void SortInputMethodIdsByNamesInternal( collator.get(), id_to_language_code_map)); } -void ReorderInputMethodIdsForLanguageCode( - const std::string& language_code, - std::vector<std::string>* input_method_ids) { - for (size_t i = 0; i < arraysize(kLanguageDefaultInputMethodIds); ++i) { - if (language_code == kLanguageDefaultInputMethodIds[i].language_code) { - std::vector<std::string>::iterator iter = - std::find(input_method_ids->begin(), input_method_ids->end(), - kLanguageDefaultInputMethodIds[i].input_method_id); - // If it's not on the top of |input_method_id|, swap it with the top one. - if (iter != input_method_ids->end() && - iter != input_method_ids->begin()) { - std::swap(*input_method_ids->begin(), *iter); - } - break; // Don't have to check other language codes. - } - } -} - bool GetInputMethodIdsFromLanguageCode( const std::string& normalized_language_code, InputMethodType type, @@ -594,9 +570,11 @@ void EnableInputMethods(const std::string& language_code, InputMethodType type, std::vector<std::string> input_method_ids; GetInputMethodIdsFromLanguageCode(language_code, type, &input_method_ids); + std::string keyboard = CrosLibrary::Get()->GetKeyboardLibrary()-> + GetHardwareKeyboardLayoutName(); if (std::count(input_method_ids.begin(), input_method_ids.end(), - kHardwareKeyboardLayout) == 0) { - input_method_ids.push_back(kHardwareKeyboardLayout); + keyboard) == 0) { + input_method_ids.push_back(keyboard); } // First, sort the vector by input method id, then by its display name. std::sort(input_method_ids.begin(), input_method_ids.end()); @@ -607,11 +585,16 @@ void EnableInputMethods(const std::string& language_code, InputMethodType type, value.type = ImeConfigValue::kValueTypeStringList; value.string_list_value = input_method_ids; InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary(); - library->SetImeConfig(kGeneralSectionName, kPreloadEnginesConfigName, value); + library->SetImeConfig(language_prefs::kGeneralSectionName, + language_prefs::kPreloadEnginesConfigName, value); if (!initial_input_method_id.empty()) { library->ChangeInputMethod(initial_input_method_id); } } +void OnLocaleChanged() { + Singleton<IdMaps>::get()->ReloadMaps(); +} + } // namespace input_method } // namespace chromeos diff --git a/chrome/browser/chromeos/input_method/input_method_util.h b/chrome/browser/chromeos/input_method/input_method_util.h index 72d86ea..f97a792 100644 --- a/chrome/browser/chromeos/input_method/input_method_util.h +++ b/chrome/browser/chromeos/input_method/input_method_util.h @@ -4,7 +4,9 @@ #ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_UTIL_H_ #define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_UTIL_H_ +#pragma once +#include <map> #include <string> #include <vector> @@ -14,8 +16,8 @@ namespace chromeos { namespace input_method { -// The list of language that do not have associated input methods. For -// these languages, we associate input methods here. +// The list of language that do not have associated input methods in IBus. +// For these languages, we associate input methods here. const struct ExtraLanguage { const char* language_code; const char* input_method_id; @@ -74,12 +76,6 @@ std::string GetLanguageCodeFromDescriptor( // "pinyin" => "" std::string GetKeyboardLayoutName(const std::string& input_method_id); -// Rewrites the language name and returns the modified version if -// necessary. Otherwise, returns the given language name as is. -// In particular, this rewrites the special language name used for input -// methods that don't fall under any other languages. -std::wstring MaybeRewriteLanguageName(const std::wstring& language_name); - // Converts an input method ID to a language code of the IME. Returns "Eng" // when |input_method_id| is unknown. // Example: "hangul" => "ko" @@ -95,10 +91,17 @@ std::string GetInputMethodDisplayNameFromId(const std::string& input_method_id); // Converts a language code to a language display name, using the // current application locale. MaybeRewriteLanguageName() is called // internally. -// Examples: "fr" => "French" +// Examples: "fi" => "Finnish" // "en-US" => "English (United States)" std::wstring GetLanguageDisplayNameFromCode(const std::string& language_code); +// Converts a language code to a language native display name. +// MaybeRewriteLanguageName() is called internally. +// Examples: "fi" => "suomi" (rather than Finnish) +// "en-US" => "English (United States)" +std::wstring GetLanguageNativeDisplayNameFromCode( + const std::string& language_code); + // Sorts the given language codes by their corresponding language names, // using the unicode string comparator. Uses unstable sorting. void SortLanguageCodesByNames(std::vector<std::string>* language_codes); @@ -107,15 +110,6 @@ void SortLanguageCodesByNames(std::vector<std::string>* language_codes); // using the unicode string comparator. Uses stable sorting. void SortInputMethodIdsByNames(std::vector<std::string>* input_method_ids); -// Reorders the given input method ids for the language code. For -// example, if |language_codes| is "fr" and |input_method_ids| contains -// ["xkb:be::fra", and "xkb:fr::fra"], the list is reordered to -// ["xkb:fr::fra", and "xkb:be::fra"], so that French keyboard layout -// comes before Belgian keyboard layout. -void ReorderInputMethodIdsForLanguageCode( - const std::string& language_code, - std::vector<std::string>* input_method_ids); - enum InputMethodType { kKeyboardLayoutsOnly, kAllInputMethods, @@ -156,6 +150,8 @@ bool GetInputMethodIdsFromLanguageCodeInternal( InputMethodType type, std::vector<std::string>* out_input_method_ids); +void OnLocaleChanged(); + } // namespace input_method } // namespace chromeos diff --git a/chrome/browser/chromeos/input_method/input_method_util_unittest.cc b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc index b4a3c95..f228d11 100644 --- a/chrome/browser/chromeos/input_method/input_method_util_unittest.cc +++ b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc @@ -34,6 +34,9 @@ TEST(InputMethodUtilTest, NormalizeLanguageCode) { // TODO(yusukes): test all language codes that IBus provides. EXPECT_EQ("ja", NormalizeLanguageCode("ja")); EXPECT_EQ("ja", NormalizeLanguageCode("jpn")); + // In the past "t" had a meaning of "other language" for some m17n latin + // input methods for testing purpose, but it is no longer used. We test "t" + // here as just an "unknown" language. EXPECT_EQ("t", NormalizeLanguageCode("t")); EXPECT_EQ("zh-CN", NormalizeLanguageCode("zh-CN")); EXPECT_EQ("zh-CN", NormalizeLanguageCode("zh_CN")); @@ -74,12 +77,6 @@ TEST(InputMethodUtilTest, GetLanguageCodeFromDescriptor) { InputMethodDescriptor("xkb:uk::eng", "United Kingdom", "us", "eng"))); } -TEST(InputMethodUtilTest, MaybeRewriteLanguageName) { - EXPECT_EQ(L"English", MaybeRewriteLanguageName(L"English")); - EXPECT_EQ(l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS), - MaybeRewriteLanguageName(L"t")); -} - TEST(InputMethodUtilTest, GetKeyboardLayoutName) { // Unsupported cases EXPECT_EQ("", GetKeyboardLayoutName("UNSUPPORTED_ID")); @@ -88,8 +85,6 @@ TEST(InputMethodUtilTest, GetKeyboardLayoutName) { EXPECT_EQ("", GetKeyboardLayoutName("mozc")); EXPECT_EQ("", GetKeyboardLayoutName("mozc-jp")); EXPECT_EQ("", GetKeyboardLayoutName("pinyin")); - EXPECT_EQ("", GetKeyboardLayoutName("m17n:t:latn-pre")); - EXPECT_EQ("", GetKeyboardLayoutName("m17n:t:latn-post")); EXPECT_EQ("", GetKeyboardLayoutName("m17n:ar:kbd")); EXPECT_EQ("", GetKeyboardLayoutName("m17n:he:kbd")); EXPECT_EQ("", GetKeyboardLayoutName("m17n:hi:itrans")); @@ -142,10 +137,11 @@ TEST(InputMethodUtilTest, GetKeyboardLayoutName) { } TEST(InputMethodUtilTest, GetLanguageDisplayNameFromCode) { - EXPECT_EQ(L"French", GetLanguageDisplayNameFromCode("fr")); - // MaybeRewriteLanguageName() should be applied. - EXPECT_EQ(l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS), - GetLanguageDisplayNameFromCode("t")); + EXPECT_EQ(L"Finnish", GetLanguageDisplayNameFromCode("fi")); +} + +TEST(InputMethodUtilTest, GetLanguageNativeDisplayNameFromCode) { + EXPECT_EQ(L"suomi", GetLanguageNativeDisplayNameFromCode("fi")); } TEST(InputMethodUtilTest, SortLanguageCodesByNames) { @@ -155,6 +151,7 @@ TEST(InputMethodUtilTest, SortLanguageCodesByNames) { language_codes.push_back("ja"); language_codes.push_back("fr"); + // For "t", see the comment in NormalizeLanguageCode test. language_codes.push_back("t"); SortLanguageCodesByNames(&language_codes); ASSERT_EQ(3U, language_codes.size()); @@ -178,7 +175,6 @@ TEST(LanguageConfigModelTest, SortInputMethodIdsByNamesInternal) { id_to_language_code_map.insert(std::make_pair("mozc-jp", "ja")); id_to_language_code_map.insert(std::make_pair("xkb:jp::jpn", "ja")); id_to_language_code_map.insert(std::make_pair("xkb:fr::fra", "fr")); - id_to_language_code_map.insert(std::make_pair("m17n:latn-pre", "t")); std::vector<std::string> input_method_ids; // Check if this function can handle an empty list. @@ -187,87 +183,30 @@ TEST(LanguageConfigModelTest, SortInputMethodIdsByNamesInternal) { input_method_ids.push_back("mozc"); // Japanese input_method_ids.push_back("xkb:fr::fra"); // French - input_method_ids.push_back("m17n:latn-pre"); // Others SortInputMethodIdsByNamesInternal(id_to_language_code_map, &input_method_ids); - ASSERT_EQ(3U, input_method_ids.size()); + ASSERT_EQ(2U, input_method_ids.size()); ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French ASSERT_EQ("mozc", input_method_ids[1]); // Japanese - ASSERT_EQ("m17n:latn-pre", input_method_ids[2]); // Others // Add a duplicate entry and see if it works. // Note that SortInputMethodIdsByNamesInternal uses std::stable_sort. input_method_ids.push_back("xkb:jp::jpn"); // also Japanese SortInputMethodIdsByNamesInternal(id_to_language_code_map, &input_method_ids); - ASSERT_EQ(4U, input_method_ids.size()); + ASSERT_EQ(3U, input_method_ids.size()); ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French ASSERT_EQ("mozc", input_method_ids[1]); // Japanese ASSERT_EQ("xkb:jp::jpn", input_method_ids[2]); // Japanese - ASSERT_EQ("m17n:latn-pre", input_method_ids[3]); // Others input_method_ids.push_back("mozc-jp"); // also Japanese SortInputMethodIdsByNamesInternal(id_to_language_code_map, &input_method_ids); - ASSERT_EQ(5U, input_method_ids.size()); + ASSERT_EQ(4U, input_method_ids.size()); ASSERT_EQ("xkb:fr::fra", input_method_ids[0]); // French ASSERT_EQ("mozc", input_method_ids[1]); // Japanese ASSERT_EQ("xkb:jp::jpn", input_method_ids[2]); // Japanese ASSERT_EQ("mozc-jp", input_method_ids[3]); // Japanese - ASSERT_EQ("m17n:latn-pre", input_method_ids[4]); // Others -} - -TEST(InputMethodUtilTest, ReorderInputMethodIdsForLanguageCode_DE) { - std::vector<std::string> input_method_ids; - input_method_ids.push_back("xkb:ch::ger"); // Switzerland - German - input_method_ids.push_back("xkb:de::ger"); // Germany - German - ReorderInputMethodIdsForLanguageCode("de", &input_method_ids); - // The list should be reordered. - ASSERT_EQ(2U, input_method_ids.size()); - EXPECT_EQ("xkb:de::ger", input_method_ids[0]); - EXPECT_EQ("xkb:ch::ger", input_method_ids[1]); -} - -TEST(InputMethodUtilTest, ReorderInputMethodIdsForLanguageCode_FR) { - std::vector<std::string> input_method_ids; - input_method_ids.push_back("xkb:be::fra"); // Belgium - French - input_method_ids.push_back("xkb:fr::fra"); // France - French - ReorderInputMethodIdsForLanguageCode("fr", &input_method_ids); - // The list should be reordered. - ASSERT_EQ(2U, input_method_ids.size()); - EXPECT_EQ("xkb:fr::fra", input_method_ids[0]); - EXPECT_EQ("xkb:be::fra", input_method_ids[1]); -} - -TEST(InputMethodUtilTest, ReorderInputMethodIdsForLanguageCode_EN_US) { - std::vector<std::string> input_method_ids; - input_method_ids.push_back("xkb:us:dvorak:eng"); // US - Dvorak - English - input_method_ids.push_back("xkb:us::eng"); // US - English - ReorderInputMethodIdsForLanguageCode("en-US", &input_method_ids); - // The list should be reordered. - ASSERT_EQ(2U, input_method_ids.size()); - EXPECT_EQ("xkb:us::eng", input_method_ids[0]); - EXPECT_EQ("xkb:us:dvorak:eng", input_method_ids[1]); -} - -TEST(InputMethodUtilTest, ReorderInputMethodIdsForLanguageCode_FI) { - std::vector<std::string> input_method_ids; - input_method_ids.push_back("xkb:fi::fin"); // Finland - Finnish - ReorderInputMethodIdsForLanguageCode("fi", &input_method_ids); - // There is no rule for reordering for Finnish. - ASSERT_EQ(1U, input_method_ids.size()); - EXPECT_EQ("xkb:fi::fin", input_method_ids[0]); -} - -TEST(InputMethodUtilTest, ReorderInputMethodIdsForLanguageCode_Noop) { - std::vector<std::string> input_method_ids; - input_method_ids.push_back("xkb:fr::fra"); // France - French - input_method_ids.push_back("xkb:be::fra"); // Belgium - French - // If the list is already sorted, nothing should happen. - ReorderInputMethodIdsForLanguageCode("fr", &input_method_ids); - ASSERT_EQ(2U, input_method_ids.size()); - EXPECT_EQ("xkb:fr::fra", input_method_ids[0]); - EXPECT_EQ("xkb:be::fra", input_method_ids[1]); } TEST(LanguageConfigModelTest, GetInputMethodIdsForLanguageCode) { diff --git a/chrome/browser/chromeos/language_preferences.h b/chrome/browser/chromeos/language_preferences.h index 34ab1ec..2dfb95e 100644 --- a/chrome/browser/chromeos/language_preferences.h +++ b/chrome/browser/chromeos/language_preferences.h @@ -4,17 +4,20 @@ #ifndef CHROME_BROWSER_CHROMEOS_LANGUAGE_PREFERENCES_H_ #define CHROME_BROWSER_CHROMEOS_LANGUAGE_PREFERENCES_H_ +#pragma once -#include "base/basictypes.h" -#include "chrome/common/pref_names.h" -#include "grit/generated_resources.h" +#include <stddef.h> // For size_t -// Section and config names for the IBus configuration daemon. +// This file defines types and declare variables used in "Languages and +// Input" settings in Chromium OS. namespace chromeos { +namespace language_prefs { +// The struct is used for preferences consisting of multiple choices, like +// punctuation types used in Japanese input method. template <typename DataType> struct LanguageMultipleChoicePreference { - const wchar_t* pref_name; // Chrome preference name. + const char* pref_name; // Chrome preference name. DataType default_pref_value; const char* ibus_config_name; // Currently we have 10 combobox items at most. @@ -26,15 +29,19 @@ struct LanguageMultipleChoicePreference { int label_message_id; // Resource grd ID for the label. }; +// The struct is used for preferences of boolean values, like switches to +// enable or disable particular features. struct LanguageBooleanPrefs { - const wchar_t* pref_name; // Chrome preference name. + const char* pref_name; // Chrome preference name. bool default_pref_value; const char* ibus_config_name; int message_id; }; +// The struct is used for preferences of integer range values, like the +// key repeat rate. struct LanguageIntegerRangePreference { - const wchar_t* pref_name; // Chrome preference name. + const char* pref_name; // Chrome preference name. int default_pref_value; int min_pref_value; int max_pref_value; @@ -42,371 +49,121 @@ struct LanguageIntegerRangePreference { int message_id; }; +// --------------------------------------------------------------------------- // For ibus-daemon -const char kGeneralSectionName[] = "general"; -const char kHotKeySectionName[] = "general/hotkey"; -const char kPreloadEnginesConfigName[] = "preload_engines"; -const char kNextEngineInMenuConfigName[] = "next_engine_in_menu"; -const char kPreviousEngineConfigName[] = "previous_engine"; - -// TODO(yusukes): Check if the "Kana/Eisu" key in the Japanese keyboard for -// Chrome OS actually generates Zenkaku_Hankaku when the keyboard gets ready. - -// ibus-daemon accepts up to 5 next-engine hot-keys. -const char kHotkeyNextEngineInMenu[] = - "Shift+Alt+Release+Shift_L,Shift+Alt+Release+Meta_L,Control+Shift+space," - "Zenkaku_Hankaku"; -// TODO(suzhe): Add more key bindings? -const char kHotkeyPreviousEngine[] = "Control+space"; - -// For Simplified Chinese input method (ibus-chewing) -const char kChewingSectionName[] = "engine/Chewing"; - -// We have to sync the |ibus_config_name|s with those in -// ibus-chewing/files/src/Config.cc. -const LanguageBooleanPrefs kChewingBooleanPrefs[] = { - { prefs::kLanguageChewingAutoShiftCur, false, "autoShiftCur", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_AUTO_SHIFT_CUR}, - { prefs::kLanguageChewingAddPhraseDirection, false, "addPhraseDirection", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_ADD_PHRASE_DIRECTION}, - { prefs::kLanguageChewingEasySymbolInput, true, "easySymbolInput", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_EASY_SYMBOL_INPUT}, - { prefs::kLanguageChewingEscCleanAllBuf, false, "escCleanAllBuf", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_ESC_CLEAN_ALL_BUF}, - { prefs::kLanguageChewingForceLowercaseEnglish, false, - "forceLowercaseEnglish", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_FORCE_LOWER_CASE_ENGLISH}, - { prefs::kLanguageChewingPlainZhuyin, false, "plainZhuyin", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_PLAIN_ZHUYIN}, - { prefs::kLanguageChewingPhraseChoiceRearward, true, "phraseChoiceRearward", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_PHRASE_CHOICE_REARWARD}, - { prefs::kLanguageChewingSpaceAsSelection, true, "spaceAsSelection", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_SPACE_AS_SELECTION}, -}; -const size_t kNumChewingBooleanPrefs = ARRAYSIZE_UNSAFE(kChewingBooleanPrefs); - -const LanguageIntegerRangePreference kChewingIntegerPrefs[] = { - { prefs::kLanguageChewingMaxChiSymbolLen, 20, 8, 40, "maxChiSymbolLen", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_MAX_CHI_SYMBOL_LEN}, - { prefs::kLanguageChewingCandPerPage, 10, 8, 10, "candPerPage", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SETTING_CAND_PER_PAGE}, -}; -const size_t kNumChewingIntegerPrefs = ARRAYSIZE_UNSAFE(kChewingIntegerPrefs); - -const LanguageMultipleChoicePreference<const char*> - kChewingMultipleChoicePrefs[] = { - { prefs::kLanguageChewingKeyboardType, - "default", - "KBType", - {{ "default", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_DEFAULT }, - { "hsu", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_HSU }, - { "ibm", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_IBM }, - { "gin_yieh", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_GIN_YIEH }, - { "eten", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_ETEN }, - { "eten26", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_ETEN26 }, - { "dvorak", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_DVORAK }, - { "dvorak_hsu", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_DVORAK_HSU }, - { "dachen_26", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_DACHEN_26 }, - { "hanyu", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE_HANYU }}, - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_KEYBOARD_TYPE, - }, - { prefs::kLanguageChewingSelKeys, - "1234567890", - "selKeys", - {{ "1234567890", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_1234567890 }, - { "asdfghjkl;", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_ASDFGHJKLS }, - { "asdfzxcv89", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_ASDFZXCV89 }, - { "asdfjkl789", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_ASDFJKL789 }, - { "aoeu;qjkix", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_AOEUSQJKIX }, - { "aoeuhtnsid", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_AOEUHTNSID }, - { "aoeuidhtns", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_AOEUIDHTNS }, - { "1234qweras", - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS_1234QWERAS }}, - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_SEL_KEYS, - }, -}; -const size_t kNumChewingMultipleChoicePrefs = - arraysize(kChewingMultipleChoicePrefs); - -const LanguageMultipleChoicePreference<int> kChewingHsuSelKeyType = { - prefs::kLanguageChewingHsuSelKeyType, - 1, - "hsuSelKeyType", - {{ 1, IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_HSU_SEL_KEY_TYPE_1 }, - { 2, IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_HSU_SEL_KEY_TYPE_2 }}, - IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_HSU_SEL_KEY_TYPE, -}; - +// --------------------------------------------------------------------------- +extern const char kGeneralSectionName[]; +extern const char kHotKeySectionName[]; +extern const char kPreloadEnginesConfigName[]; +extern const char kNextEngineInMenuConfigName[]; +extern const char kPreviousEngineConfigName[]; +extern const char kHotkeyNextEngineInMenu[]; +extern const char kHotkeyPreviousEngine[]; + +// --------------------------------------------------------------------------- +// For Traditional Chinese input method (ibus-chewing) +// --------------------------------------------------------------------------- +extern const char kChewingSectionName[]; + +extern const LanguageBooleanPrefs kChewingBooleanPrefs[]; +// This is not ideal, but we should hard-code the number here as the value +// is referenced in other header files as array sizes. We have a +// COMPILE_ASSERT in .cc to ensure that the number is correct. +const size_t kNumChewingBooleanPrefs = 8; + +extern const LanguageIntegerRangePreference kChewingIntegerPrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumChewingIntegerPrefs = 2; +const int kChewingMaxChiSymbolLenIndex = 0; +const int kChewingCandPerPageIndex = 1; + +extern const LanguageMultipleChoicePreference<const char*> + kChewingMultipleChoicePrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumChewingMultipleChoicePrefs = 2; + +extern const LanguageMultipleChoicePreference<int> kChewingHsuSelKeyType; + +// --------------------------------------------------------------------------- // For Korean input method (ibus-hangul) -const char kHangulSectionName[] = "engine/Hangul"; -const char kHangulKeyboardConfigName[] = "HangulKeyboard"; -const char kHangulHanjaKeysConfigName[] = "HanjaKeys"; -// We add Control+Alt+9 in addition to the two default keys since Hanja key -// might not be available on the Chrome OS keyboard and F9 key is reserved by -// the window manager. -// TODO: Hanja keys are not configurable yet (and we're not sure if it should.) -const char kHangulHanjaKeys[] = "F9,Hangul_Hanja,Control+Alt+9"; +// --------------------------------------------------------------------------- +extern const char kHangulSectionName[]; +extern const char kHangulKeyboardConfigName[]; +extern const char kHangulHanjaKeysConfigName[]; +extern const char kHangulHanjaKeys[]; -const struct HangulKeyboardNameIDPair { +struct HangulKeyboardNameIDPair { int message_id; const char* keyboard_id; -} kHangulKeyboardNameIDPairs[] = { - // We have to sync the |keyboard_id|s with those in - // ibus-hangul/files/setup/main.py. - { IDS_OPTIONS_SETTINGS_LANGUAGES_HANGUL_SETTINGS_KEYBOARD_2_SET, "2" }, - { IDS_OPTIONS_SETTINGS_LANGUAGES_HANGUL_SETTINGS_KEYBOARD_3_SET_FINAL, - "3f" }, - { IDS_OPTIONS_SETTINGS_LANGUAGES_HANGUL_SETTINGS_KEYBOARD_3_SET_390, "39" }, - { IDS_OPTIONS_SETTINGS_LANGUAGES_HANGUL_SETTINGS_KEYBOARD_3_SET_NO_SHIFT, - "3s" }, - // We don't support "Sebeolsik 2 set" keyboard. }; +extern const HangulKeyboardNameIDPair kHangulKeyboardNameIDPairs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumHangulKeyboardNameIDPairs = 4; + +// --------------------------------------------------------------------------- // For Simplified Chinese input method (ibus-pinyin) -const char kPinyinSectionName[] = "engine/Pinyin"; +// --------------------------------------------------------------------------- +extern const char kPinyinSectionName[]; -// We have to sync the |ibus_config_name|s with those in -// ibus-pinyin/files/src/Config.cc. -const LanguageBooleanPrefs kPinyinBooleanPrefs[] = { - { prefs::kLanguagePinyinCorrectPinyin, true, "CorrectPinyin", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_CORRECT_PINYIN }, - { prefs::kLanguagePinyinFuzzyPinyin, false, "FuzzyPinyin", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_FUZZY_PINYIN }, - { prefs::kLanguagePinyinShiftSelectCandidate, false, "ShiftSelectCandidate", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_SHIFT_SELECT_PINYIN }, - { prefs::kLanguagePinyinMinusEqualPage, true, "MinusEqualPage", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_MINUS_EQUAL_PAGE }, - { prefs::kLanguagePinyinCommaPeriodPage, true, "CommaPeriodPage", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_COMMA_PERIOD_PAGE }, - { prefs::kLanguagePinyinAutoCommit, false, "AutoCommit", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_AUTO_COMMIT }, - { prefs::kLanguagePinyinDoublePinyin, false, "DoublePinyin", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_DOUBLE_PINYIN }, - { prefs::kLanguagePinyinInitChinese, true, "InitChinese", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_INIT_CHINESE }, - { prefs::kLanguagePinyinInitFull, false, "InitFull", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_INIT_FULL }, - { prefs::kLanguagePinyinInitFullPunct, true, "InitFullPunct", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_INIT_FULL_PUNCT }, - { prefs::kLanguagePinyinInitSimplifiedChinese, true, "InitSimplifiedChinese", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_INIT_SIMPLIFIED_CHINESE }, - { prefs::kLanguagePinyinTradCandidate, false, "TradCandidate", - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_SETTING_TRAD_CANDIDATE }, - // TODO(yusukes): Support PINYIN_{INCOMPLETE,CORRECT,FUZZY}_... prefs (32 - // additional boolean prefs.) -}; -const size_t kNumPinyinBooleanPrefs = ARRAYSIZE_UNSAFE(kPinyinBooleanPrefs); -// TODO(yusukes): Support HalfWidthPuncts and IncompletePinyin prefs if needed. +extern const LanguageBooleanPrefs kPinyinBooleanPrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumPinyinBooleanPrefs = 11; -const LanguageMultipleChoicePreference<int> kPinyinDoublePinyinSchema = { - prefs::kLanguagePinyinDoublePinyinSchema, - 0, - "DoublePinyinSchema", - {{ 0, IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA_MSPY}, - { 1, IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA_ZRM}, - { 2, IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA_ABC}, - { 3, IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA_ZGPY}, - { 4, IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA_PYJJ}}, - IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DOUBLE_SCHEMA, -}; +extern const LanguageMultipleChoicePreference<int> kPinyinDoublePinyinSchema; -const struct { - const wchar_t* pref_name; // Chrome preference name. +struct PinyinIntegerPref { + const char* pref_name; // Chrome preference name. int default_pref_value; const char* ibus_config_name; // TODO(yusukes): Add message_id if needed. -} kPinyinIntegerPrefs[] = { - // TODO(yusukes): the type of lookup_table_page_size on ibus should be uint. - { prefs::kLanguagePinyinLookupTablePageSize, 5, "LookupTablePageSize" }, -}; -const size_t kNumPinyinIntegerPrefs = ARRAYSIZE_UNSAFE(kPinyinIntegerPrefs); - -// For Japanese input method (ibus-mozc) -const char kMozcSectionName[] = "engine/Mozc"; - -#define IDS_MOZC(suffix) \ - IDS_OPTIONS_SETTINGS_LANGUAGES_MOZC_##suffix - -const LanguageBooleanPrefs kMozcBooleanPrefs[] = { - { prefs::kLanguageMozcIncognitoMode, - false, - "incognito_mode", - IDS_MOZC(INCOGNITO_MODE) - }, - { prefs::kLanguageMozcUseAutoImeTurnOff, - true, - "use_auto_ime_turn_off", - IDS_MOZC(USE_AUTO_IME_TURN_OFF) - }, - { prefs::kLanguageMozcUseDateConversion, - true, - "use_date_conversion", - IDS_MOZC(USE_DATE_CONVERSION) - }, - { prefs::kLanguageMozcUseSingleKanjiConversion, - true, - "use_single_kanji_conversion", - IDS_MOZC(USE_SINGLE_KANJI_CONVERSION) - }, - { prefs::kLanguageMozcUseSymbolConversion, - true, - "use_symbol_conversion", - IDS_MOZC(USE_SYMBOL_CONVERSION) - }, - { prefs::kLanguageMozcUseNumberConversion, - true, - "use_number_conversion", - IDS_MOZC(USE_NUMBER_CONVERSION) - }, - { prefs::kLanguageMozcUseHistorySuggest, - true, - "use_history_suggest", - IDS_MOZC(USE_HISTORY_SUGGEST) - }, - { prefs::kLanguageMozcUseDictionarySuggest, - true, - "use_dictionary_suggest", - IDS_MOZC(USE_DICTIONARY_SUGGEST) - }, }; -const size_t kNumMozcBooleanPrefs = ARRAYSIZE_UNSAFE(kMozcBooleanPrefs); -const LanguageMultipleChoicePreference<const char*> - kMozcMultipleChoicePrefs[] = { - { prefs::kLanguageMozcPreeditMethod, - "ROMAN", - "preedit_method", - {{ "ROMAN", IDS_MOZC(PREEDIT_METHOD_ROMAN) }, - { "KANA", IDS_MOZC(PREEDIT_METHOD_KANA) }}, - IDS_MOZC(PREEDIT_METHOD), - }, - { prefs::kLanguageMozcSessionKeymap, - "MSIME", - "session_keymap", - {{ "ATOK", IDS_MOZC(SESSION_KEYMAP_ATOK) }, - { "MSIME", IDS_MOZC(SESSION_KEYMAP_MSIME) }, - { "KOTOERI", IDS_MOZC(SESSION_KEYMAP_KOTOERI) }}, - // TODO: Support "CUSTOM" keymap. - IDS_MOZC(SESSION_KEYMAP), - }, - { prefs::kLanguageMozcPunctuationMethod, - "KUTEN_TOUTEN", - "punctuation_method", - {{ "KUTEN_TOUTEN", - IDS_MOZC(PUNCTUATION_METHOD_KUTEN_TOUTEN) }, - { "COMMA_PERIOD", - IDS_MOZC(PUNCTUATION_METHOD_COMMA_PERIOD) }, - { "KUTEN_PERIOD", - IDS_MOZC(PUNCTUATION_METHOD_KUTEN_PERIOD) }, - { "COMMA_TOUTEN", - IDS_MOZC(PUNCTUATION_METHOD_COMMA_TOUTEN) }}, - IDS_MOZC(PUNCTUATION_METHOD), - }, - { prefs::kLanguageMozcSymbolMethod, - "CORNER_BRACKET_MIDDLE_DOT", - "symbol_method", - {{ "CORNER_BRACKET_MIDDLE_DOT", - IDS_MOZC(SYMBOL_METHOD_CORNER_BRACKET_MIDDLE_DOT) }, - { "SQUARE_BRACKET_SLASH", - IDS_MOZC(SYMBOL_METHOD_SQUARE_BRACKET_SLASH) }, - { "CORNER_BRACKET_SLASH", - IDS_MOZC(SYMBOL_METHOD_CORNER_BRACKET_SLASH) }, - { "SQUARE_BRACKET_MIDDLE_DOT", - IDS_MOZC(SYMBOL_METHOD_SQUARE_BRACKET_MIDDLE_DOT) }}, - IDS_MOZC(SYMBOL_METHOD), - }, - { prefs::kLanguageMozcSpaceCharacterForm, - "FUNDAMENTAL_INPUT_MODE", - "space_character_form", - {{ "FUNDAMENTAL_INPUT_MODE", - IDS_MOZC(SPACE_CHARACTER_FORM_FUNDAMENTAL_INPUT_MODE) }, - { "FUNDAMENTAL_FULL_WIDTH", - IDS_MOZC(SPACE_CHARACTER_FORM_FUNDAMENTAL_FULL_WIDTH) }, - { "FUNDAMENTAL_HALF_WIDTH", - IDS_MOZC(SPACE_CHARACTER_FORM_FUNDAMENTAL_HALF_WIDTH) }}, - IDS_MOZC(SPACE_CHARACTER_FORM), - }, - { prefs::kLanguageMozcHistoryLearningLevel, - "DEFAULT_HISTORY", - "history_learning_level", - {{ "DEFAULT_HISTORY", - IDS_MOZC(HISTORY_LEARNING_LEVEL_DEFAULT_HISTORY) }, - { "READ_ONLY", - IDS_MOZC(HISTORY_LEARNING_LEVEL_READ_ONLY) }, - { "NO_HISTORY", - IDS_MOZC(HISTORY_LEARNING_LEVEL_NO_HISTORY) }}, - IDS_MOZC(HISTORY_LEARNING_LEVEL), - }, - // TODO(mazda): Uncomment this block once the candidate window in Chrome OS - // supports changing shortcut labels. - // { prefs::kLanguageMozcSelectionShortcut, - // "SHORTCUT_123456789", - // "selection_shortcut", - // {{ "NO_SHORTCUT", - // IDS_MOZC(SELECTION_SHORTCUT_NO_SHORTCUT) }, - // { "SHORTCUT_123456789", - // IDS_MOZC(SELECTION_SHORTCUT_SHORTCUT_123456789) }, - // { "SHORTCUT_ASDFGHJKL", - // IDS_MOZC(SELECTION_SHORTCUT_SHORTCUT_ASDFGHJKL) }}, - // IDS_MOZC(SELECTION_SHORTCUT), - // }, - { prefs::kLanguageMozcShiftKeyModeSwitch, - "ASCII_INPUT_MODE", - "shift_key_mode_switch", - {{ "OFF", - IDS_MOZC(SHIFT_KEY_MODE_SWITCH_OFF) }, - { "ASCII_INPUT_MODE", - IDS_MOZC(SHIFT_KEY_MODE_SWITCH_ASCII_INPUT_MODE) }, - { "KATAKANA_INPUT_MODE", - IDS_MOZC(SHIFT_KEY_MODE_SWITCH_KATAKANA_INPUT_MODE) }}, - IDS_MOZC(SHIFT_KEY_MODE_SWITCH), - }, - { prefs::kLanguageMozcNumpadCharacterForm, - "NUMPAD_HALF_WIDTH", - "numpad_character_form", - {{ "NUMPAD_INPUT_MODE", - IDS_MOZC(NUMPAD_CHARACTER_FORM_NUMPAD_INPUT_MODE) }, - { "NUMPAD_FULL_WIDTH", - IDS_MOZC(NUMPAD_CHARACTER_FORM_NUMPAD_FULL_WIDTH) }, - { "NUMPAD_HALF_WIDTH", - IDS_MOZC(NUMPAD_CHARACTER_FORM_NUMPAD_HALF_WIDTH) }, - { "NUMPAD_DIRECT_INPUT", - IDS_MOZC(NUMPAD_CHARACTER_FORM_NUMPAD_DIRECT_INPUT) }}, - IDS_MOZC(NUMPAD_CHARACTER_FORM), - }, -}; -const size_t kNumMozcMultipleChoicePrefs = arraysize(kMozcMultipleChoicePrefs); +extern const PinyinIntegerPref kPinyinIntegerPrefs[]; +const size_t kNumPinyinIntegerPrefs = 1; -const LanguageIntegerRangePreference kMozcIntegerPrefs[] = { - { prefs::kLanguageMozcSuggestionsSize, 3, 1, 9, "suggestions_size", - IDS_MOZC(SUGGESTIONS_SIZE)}, +// --------------------------------------------------------------------------- +// For Japanese input method (ibus-mozc) +// --------------------------------------------------------------------------- +extern const char kMozcSectionName[]; + +extern const LanguageBooleanPrefs kMozcBooleanPrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumMozcBooleanPrefs = 8; + +extern const LanguageMultipleChoicePreference<const char*> + kMozcMultipleChoicePrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumMozcMultipleChoicePrefs = 8; + +extern const LanguageIntegerRangePreference kMozcIntegerPrefs[]; +// See comments at kNumChewingBooleanPrefs for why we hard-code this here. +const size_t kNumMozcIntegerPrefs = 1; + +// --------------------------------------------------------------------------- +// For keyboard stuff +// --------------------------------------------------------------------------- +// TODO(yusukes): Temporary solution for View version of modifier key remapper. +// Remove RemapType and kXkbModifierMultipleChoicePrefs when we finish to +// migrate to DOMUI. +enum RemapType { + kNoRemap = 0, + kSwapCtrlAndAlt = 1, + kSwapSearchAndCtrl = 2, }; -const size_t kNumMozcIntegerPrefs = ARRAYSIZE_UNSAFE(kMozcIntegerPrefs); - -#undef IDS_MOZC +extern const LanguageMultipleChoicePreference<int> + kXkbModifierMultipleChoicePrefs; -// For Traditional Chinese input methods (ibus-pinyin-bopomofo and ibus-chewing) -// TODO(yusukes): Add constants for Traditional Chinese input methods. +// A delay between the first and the start of the rest. +extern const int kXkbAutoRepeatDelayInMs; +// An interval between the repeated keys. +extern const int kXkbAutoRepeatIntervalInMs; // A string Chrome preference (Local State) of the preferred keyboard layout in // the login screen. -const wchar_t kPreferredKeyboardLayout[] = L"PreferredKeyboardLayout"; - -// A input method name that corresponds the hardware keyboard layout. -// TODO(yusukes): just assuming US qwerty keyboard is not always correct. -const char kHardwareKeyboardLayout[] = "xkb:us::eng"; +extern const char kPreferredKeyboardLayout[]; +} // language_prefs } // chromeos #endif // CHROME_BROWSER_CHROMEOS_LANGUAGE_PREFERENCES_H_ diff --git a/chrome/browser/chromeos/language_preferences_unittest.cc b/chrome/browser/chromeos/language_preferences_unittest.cc index 49c5472..2383a28 100644 --- a/chrome/browser/chromeos/language_preferences_unittest.cc +++ b/chrome/browser/chromeos/language_preferences_unittest.cc @@ -10,6 +10,7 @@ #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { +namespace language_prefs { namespace { @@ -101,6 +102,8 @@ TEST(LanguagePreferencesTest, TestDefaultValuesOfMultipleChoicePrefs) { EXPECT_TRUE(CheckDefaultValueOfMultipleChoicePrefs( kChewingMultipleChoicePrefs, kNumChewingMultipleChoicePrefs)); EXPECT_TRUE(CheckDefaultValueOfMultipleChoicePrefs( + &kXkbModifierMultipleChoicePrefs, 1)); + EXPECT_TRUE(CheckDefaultValueOfMultipleChoicePrefs( &kChewingHsuSelKeyType, 1)); EXPECT_TRUE(CheckDefaultValueOfMultipleChoicePrefs( &kPinyinDoublePinyinSchema, 1)); @@ -114,6 +117,8 @@ TEST(LanguagePreferencesTest, TestDuplicationOfMultipleChoicePrefs) { EXPECT_TRUE(CheckDuplicationOfMultipleChoicePrefs( kChewingMultipleChoicePrefs, kNumChewingMultipleChoicePrefs)); EXPECT_TRUE(CheckDuplicationOfMultipleChoicePrefs( + &kXkbModifierMultipleChoicePrefs, 1)); + EXPECT_TRUE(CheckDuplicationOfMultipleChoicePrefs( &kChewingHsuSelKeyType, 1)); EXPECT_TRUE(CheckDuplicationOfMultipleChoicePrefs( &kPinyinDoublePinyinSchema, 1)); @@ -129,4 +134,5 @@ TEST(LanguagePreferencesTest, TestDefaultValuesOfIntegerRangePrefs) { kMozcIntegerPrefs, kNumMozcIntegerPrefs)); } +} // namespace language_prefs } // namespace chromeos diff --git a/chrome/browser/chromeos/login/account_creation_view.cc b/chrome/browser/chromeos/login/account_creation_view.cc index 054e966..82ff793 100644 --- a/chrome/browser/chromeos/login/account_creation_view.cc +++ b/chrome/browser/chromeos/login/account_creation_view.cc @@ -61,8 +61,6 @@ class AccountCreationTabContents : public WizardWebPageViewTabContents, virtual bool FillAutoFillFormData(int query_id, const webkit_glue::FormData& form, - const string16& name, - const string16& label, int unique_id) { return false; } diff --git a/chrome/browser/chromeos/login/account_creation_view.h b/chrome/browser/chromeos/login/account_creation_view.h index 11aa14d..40bbe31 100644 --- a/chrome/browser/chromeos/login/account_creation_view.h +++ b/chrome/browser/chromeos/login/account_creation_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_ACCOUNT_CREATION_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_ACCOUNT_CREATION_VIEW_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/account_screen.cc b/chrome/browser/chromeos/login/account_screen.cc index b9ad26d..d7298fc 100644 --- a/chrome/browser/chromeos/login/account_screen.cc +++ b/chrome/browser/chromeos/login/account_screen.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/login/account_screen.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/chromeos/login/account_creation_view.h" @@ -14,6 +15,7 @@ #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "googleurl/src/gurl.h" +#include "views/widget/widget_gtk.h" namespace chromeos { @@ -128,6 +130,12 @@ void AccountScreen::NavigationStateChanged(const TabContents* source, } } +void AccountScreen::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { + views::Widget* widget = view()->GetWidget(); + if (widget && event.os_event && !event.skip_in_browser) + static_cast<views::WidgetGtk*>(widget)->HandleKeyboardEvent(event.os_event); +} + /////////////////////////////////////////////////////////////////////////////// // AccountScreen, WebPageDelegate implementation: void AccountScreen::OnPageLoaded() { @@ -137,9 +145,7 @@ void AccountScreen::OnPageLoaded() { if (g_browser_process) { const std::string locale = g_browser_process->GetApplicationLocale(); input_method::EnableInputMethods( - locale, input_method::kKeyboardLayoutsOnly, ""); - // TODO(yusukes,suzhe): Change the 2nd argument to kAllInputMethods when - // crosbug.com/2670 is fixed. + locale, input_method::kAllInputMethods, ""); } view()->ShowPageContent(); } diff --git a/chrome/browser/chromeos/login/account_screen.h b/chrome/browser/chromeos/login/account_screen.h index ec9173c..8d3ca4f 100644 --- a/chrome/browser/chromeos/login/account_screen.h +++ b/chrome/browser/chromeos/login/account_screen.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_ACCOUNT_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_ACCOUNT_SCREEN_H_ +#pragma once #include <string> @@ -52,6 +53,7 @@ class AccountScreen : public ViewScreen<AccountCreationView>, virtual void NavigationStateChanged(const TabContents* source, unsigned changed_flags); virtual void LoadingStateChanged(TabContents* source); + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); // WebPageScreen implementation: virtual void CloseScreen(ScreenObserver::ExitCodes code); diff --git a/chrome/browser/chromeos/login/account_screen_browsertest.cc b/chrome/browser/chromeos/login/account_screen_browsertest.cc index 5f39335..eec150d 100644 --- a/chrome/browser/chromeos/login/account_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/account_screen_browsertest.cc @@ -5,13 +5,14 @@ #include <string> #include "chrome/browser/child_process_security_policy.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/account_screen.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_in_process_browser_test.h" -#include "chrome/browser/chrome_thread.h" #include "chrome/common/url_constants.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" +#include "net/test/test_server.h" #include "net/url_request/url_request_about_job.h" #include "net/url_request/url_request_filter.h" #include "testing/gmock/include/gmock/gmock.h" @@ -27,9 +28,8 @@ class AccountScreenTest : public WizardInProcessBrowserTest { protected: // Overridden from WizardInProcessBrowserTest: virtual void SetUpWizard() { - HTTPTestServer* server = StartHTTPServer(); - ASSERT_TRUE(server != NULL); - GURL new_account_page_url(server->TestServerPage("files/new_account.html")); + ASSERT_TRUE(test_server()->Start()); + GURL new_account_page_url(test_server()->GetURL("files/new_account.html")); AccountScreen::set_new_account_page_url(new_account_page_url); AccountScreen::set_check_for_https(false); } @@ -41,8 +41,7 @@ class AccountScreenTest : public WizardInProcessBrowserTest { // A basic test. It does not care how things evolve after the URL is // loaded. Thus no message loop is started. Just check that initial // status is expected. -// This test is flaky. See http://crbug.com/49004 . -IN_PROC_BROWSER_TEST_F(AccountScreenTest, FLAKY_TestBasic) { +IN_PROC_BROWSER_TEST_F(AccountScreenTest, TestBasic) { ASSERT_TRUE(controller()); EXPECT_EQ(controller()->GetAccountScreen(), controller()->current_screen()); } @@ -69,7 +68,7 @@ static URLRequestJob* InspectorHook(URLRequest* request, return new URLRequestAboutJob(request); } -IN_PROC_BROWSER_TEST_F(AccountScreenTest, FLAKY_TestSchemeInspector) { +IN_PROC_BROWSER_TEST_F(AccountScreenTest, TestSchemeInspector) { ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme( chrome::kCrosScheme); URLRequestFilter::GetInstance()->AddHostnameHandler(chrome::kCrosScheme, diff --git a/chrome/browser/chromeos/login/auth_response_handler.h b/chrome/browser/chromeos/login/auth_response_handler.h index 826baeb..9be1a5e 100644 --- a/chrome/browser/chromeos/login/auth_response_handler.h +++ b/chrome/browser/chromeos/login/auth_response_handler.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_AUTH_RESPONSE_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_AUTH_RESPONSE_HANDLER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/authentication_notification_details.h b/chrome/browser/chromeos/login/authentication_notification_details.h index 4ad9f0d..2b14419 100644 --- a/chrome/browser/chromeos/login/authentication_notification_details.h +++ b/chrome/browser/chromeos/login/authentication_notification_details.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATION_NOTIFICATION_DETAILS_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATION_NOTIFICATION_DETAILS_H_ +#pragma once // A class to hold the parameters we get back from an authentication attempt // through the login manager diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h index bc0bdca..5fba9c3 100644 --- a/chrome/browser/chromeos/login/authenticator.h +++ b/chrome/browser/chromeos/login/authenticator.h @@ -4,12 +4,10 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_ +#pragma once -#include <vector> - -#include "base/logging.h" +#include "base/basictypes.h" #include "base/ref_counted.h" -#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" @@ -25,10 +23,8 @@ namespace chromeos { // consumer_->OnPasswordChangeDetected() on the UI thread. class Authenticator : public base::RefCountedThreadSafe<Authenticator> { public: - explicit Authenticator(LoginStatusConsumer* consumer) - : consumer_(consumer) { - } - virtual ~Authenticator() {} + explicit Authenticator(LoginStatusConsumer* consumer); + virtual ~Authenticator(); // Given a |username| and |password|, this method attempts to authenticate // to login. @@ -55,7 +51,7 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> { // and also call back to the login UI. virtual void OnLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; - virtual void OnLoginFailure(const std::string& data) = 0; + virtual void OnLoginFailure(const LoginFailure& error) = 0; // Call these methods on the UI thread. // If a password logs the user in online, but cannot be used to @@ -75,6 +71,13 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> { virtual void ResyncEncryptedData( const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; + // Perform basic canonicalization of |email_address|, taking into account + // that gmail does not consider '.' or caps inside a username to matter. + // It also ignores everything after a '+'. + // For example, c.masone+abc@gmail.com == cMaSone@gmail.com, per + // http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313# + static std::string Canonicalize(const std::string& email_address); + protected: LoginStatusConsumer* consumer_; diff --git a/chrome/browser/chromeos/login/background_view.cc b/chrome/browser/chromeos/login/background_view.cc index 3d9f954..199f92f 100644 --- a/chrome/browser/chromeos/login/background_view.cc +++ b/chrome/browser/chromeos/login/background_view.cc @@ -4,21 +4,33 @@ #include "chrome/browser/chromeos/login/background_view.h" +#include <string> +#include <vector> #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "app/x11_util.h" +#include "base/string16.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/oobe_progress_bar.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/status/clock_menu_button.h" #include "chrome/browser/chromeos/status/feedback_menu_button.h" #include "chrome/browser/chromeos/status/language_menu_button.h" #include "chrome/browser/chromeos/status/network_menu_button.h" #include "chrome/browser/chromeos/status/status_area_view.h" #include "chrome/browser/chromeos/wm_ipc.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/dom_view.h" #include "cros/chromeos_wm_ipc_enums.h" +#include "googleurl/src/gurl.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "views/controls/button/text_button.h" #include "views/controls/label.h" #include "views/screen.h" #include "views/widget/widget_gtk.h" @@ -30,22 +42,35 @@ using views::WidgetGtk; -namespace chromeos { +namespace { -BackgroundView::BackgroundView() - : status_area_(NULL), - os_version_label_(NULL), - boot_times_label_(NULL), - did_paint_(false) { - views::Painter* painter = CreateBackgroundPainter(); - set_background(views::Background::CreateBackgroundPainter(true, painter)); - InitStatusArea(); - InitInfoLabels(); -} +// The same as TextButton but switches cursor to hand cursor when mouse +// is over the button. +class TextButtonWithHandCursorOver : public views::TextButton { + public: + TextButtonWithHandCursorOver(views::ButtonListener* listener, + const std::wstring& text) + : views::TextButton(listener, text) { + } + + virtual ~TextButtonWithHandCursorOver() {} + + virtual gfx::NativeCursor GetCursorForPoint( + views::Event::EventType event_type, + const gfx::Point& p) { + if (!IsEnabled()) { + return NULL; + } + return gdk_cursor_new(GDK_HAND2); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TextButtonWithHandCursorOver); +}; +// This gets rid of the ugly X default cursor. static void ResetXCursor() { // TODO(sky): nuke this once new window manager is in place. - // This gets rid of the ugly X default cursor. Display* display = x11_util::GetXDisplay(); Cursor cursor = XCreateFontCursor(display, XC_left_ptr); XID root_window = x11_util::GetX11RootWindow(); @@ -54,15 +79,54 @@ static void ResetXCursor() { XChangeWindowAttributes(display, root_window, CWCursor, &attr); } +} // namespace + +namespace chromeos { + +/////////////////////////////////////////////////////////////////////////////// +// BackgroundView public: + +BackgroundView::BackgroundView() + : status_area_(NULL), + os_version_label_(NULL), + boot_times_label_(NULL), + progress_bar_(NULL), + go_incognito_button_(NULL), + did_paint_(false), + delegate_(NULL), + background_area_(NULL) { +} + +void BackgroundView::Init(const GURL& background_url) { + views::Painter* painter = CreateBackgroundPainter(); + set_background(views::Background::CreateBackgroundPainter(true, painter)); + InitStatusArea(); + InitInfoLabels(); + if (!background_url.is_empty()) { + Profile* profile = ProfileManager::GetDefaultProfile(); + background_area_ = new DOMView(); + AddChildView(background_area_); + background_area_->Init(profile, NULL); + background_area_->SetVisible(false); + background_area_->LoadURL(background_url); + } +} + // static views::Widget* BackgroundView::CreateWindowContainingView( const gfx::Rect& bounds, + const GURL& background_url, BackgroundView** view) { ResetXCursor(); WidgetGtk* window = new WidgetGtk(WidgetGtk::TYPE_WINDOW); window->Init(NULL, bounds); *view = new BackgroundView(); + (*view)->Init(background_url); + + if ((*view)->ScreenSaverEnabled()) + (*view)->ShowScreenSaver(); + window->SetContentsView(*view); (*view)->UpdateWindowType(); @@ -78,6 +142,72 @@ void BackgroundView::SetStatusAreaVisible(bool visible) { status_area_->SetVisible(visible); } +void BackgroundView::SetOobeProgressBarVisible(bool visible) { + if (!progress_bar_ && visible) + InitProgressBar(); + + if (progress_bar_) + progress_bar_->SetVisible(visible); +} + +bool BackgroundView::IsOobeProgressBarVisible() { + return progress_bar_ && progress_bar_->IsVisible(); +} + +void BackgroundView::SetOobeProgress(LoginStep step) { + DCHECK(step <= PICTURE); + if (progress_bar_) + progress_bar_->SetProgress(step); +} + +// Toggles GoIncognito button visibility. +void BackgroundView::SetGoIncognitoButtonVisible(bool visible, + Delegate *delegate) { + // Set delegate to handle button pressing. + delegate_ = delegate; + bool currently_visible = + go_incognito_button_ && go_incognito_button_->IsVisible(); + if (currently_visible != visible) { + if (!go_incognito_button_) { + InitGoIncognitoButton(); + } + go_incognito_button_->SetVisible(visible); + } +} + +void BackgroundView::ShowScreenSaver() { + SetStatusAreaVisible(false); + background_area_->SetVisible(true); +} + +void BackgroundView::HideScreenSaver() { + SetStatusAreaVisible(true); + // TODO(oshima): we need a way to suspend screen saver + // to save power when it's not visible. + background_area_->SetVisible(false); +} + +bool BackgroundView::IsScreenSaverVisible() { + return ScreenSaverEnabled() && background_area_->IsVisible(); +} + +bool BackgroundView::ScreenSaverEnabled() { + return background_area_ != NULL; +} + +void BackgroundView::OnOwnerChanged() { + if (go_incognito_button_) { + // BackgroundView is passed among multiple controllers, so they should + // explicitly enable "Go incognito" button if needed. + RemoveChildView(go_incognito_button_); + delete go_incognito_button_; + go_incognito_button_ = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// BackgroundView protected: + void BackgroundView::Paint(gfx::Canvas* canvas) { views::View::Paint(canvas); if (!did_paint_) { @@ -87,14 +217,19 @@ void BackgroundView::Paint(gfx::Canvas* canvas) { } void BackgroundView::Layout() { - int corner_padding = 5; - int kInfoLeftPadding = 60; - int kInfoBottomPadding = 40; - int kInfoBetweenLinesPadding = 4; + const int kCornerPadding = 5; + const int kInfoLeftPadding = 60; + const int kInfoBottomPadding = 10; + const int kInfoBetweenLinesPadding = 4; + const int kProgressBarBottomPadding = 20; + const int kProgressBarWidth = 750; + const int kProgressBarHeight = 70; + const int kGoIncognitoButtonBottomPadding = 12; + const int kGoIncognitoButtonRightPadding = 12; gfx::Size status_area_size = status_area_->GetPreferredSize(); status_area_->SetBounds( - width() - status_area_size.width() - corner_padding, - corner_padding, + width() - status_area_size.width() - kCornerPadding, + kCornerPadding, status_area_size.width(), status_area_size.height()); gfx::Size version_size = os_version_label_->GetPreferredSize(); @@ -109,8 +244,25 @@ void BackgroundView::Layout() { boot_times_label_->SetBounds( kInfoLeftPadding, height() - (version_size.height() + kInfoBottomPadding), - width() - 2 * corner_padding, + width() - 2 * kCornerPadding, version_size.height()); + if (progress_bar_) { + progress_bar_->SetBounds( + (width() - kProgressBarWidth) / 2, + (height() - kProgressBarBottomPadding - kProgressBarHeight), + kProgressBarWidth, + kProgressBarHeight); + } + if (go_incognito_button_) { + gfx::Size go_button_size = go_incognito_button_->GetPreferredSize(); + go_incognito_button_->SetBounds( + width() - go_button_size.width()- kGoIncognitoButtonRightPadding, + height() - go_button_size.height() - kGoIncognitoButtonBottomPadding, + go_button_size.width(), + go_button_size.height()); + } + if (background_area_) + background_area_->SetBounds(this->bounds()); } void BackgroundView::ChildPreferredSizeChanged(View* child) { @@ -118,6 +270,12 @@ void BackgroundView::ChildPreferredSizeChanged(View* child) { SchedulePaint(); } +void BackgroundView::OnLocaleChanged() { + UpdateLocalizedStrings(); + Layout(); + SchedulePaint(); +} + gfx::NativeWindow BackgroundView::GetNativeWindow() const { return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); @@ -138,12 +296,6 @@ void BackgroundView::OpenButtonOptions(const views::View* button_view) const { // TODO(avayvod): Add some dialog for options or remove them completely. } -bool BackgroundView::IsButtonVisible(const views::View* button_view) const { - if (button_view == status_area_->feedback_view()) - return false; - return true; -} - bool BackgroundView::IsBrowserMode() const { return false; } @@ -152,15 +304,25 @@ bool BackgroundView::IsScreenLockerMode() const { return false; } -void BackgroundView::LocaleChanged() { - Layout(); - SchedulePaint(); +void BackgroundView::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (sender == go_incognito_button_) { + DCHECK(delegate_); + if (delegate_) { + delegate_->OnGoIncognitoButton(); + } + } } +/////////////////////////////////////////////////////////////////////////////// +// BackgroundView private: + void BackgroundView::InitStatusArea() { DCHECK(status_area_ == NULL); status_area_ = new StatusAreaView(this); status_area_->Init(); + // Feedback button shoudn't be visible on OOBE/login/screen lock. + status_area_->feedback_view()->SetVisible(false); AddChildView(status_area_); } @@ -190,6 +352,66 @@ void BackgroundView::InitInfoLabels() { } } +void BackgroundView::InitProgressBar() { + std::vector<int> steps; + steps.push_back(IDS_OOBE_SELECT_NETWORK); +#if defined(OFFICIAL_BUILD) + steps.push_back(IDS_OOBE_EULA); +#endif + steps.push_back(IDS_OOBE_SIGNIN); +#if defined(OFFICIAL_BUILD) + steps.push_back(IDS_OOBE_REGISTRATION); +#endif + steps.push_back(IDS_OOBE_PICTURE); + progress_bar_ = new OobeProgressBar(steps); + AddChildView(progress_bar_); +} + +void BackgroundView::InitGoIncognitoButton() { + SkColor kButtonColor = 0xFF4F6985; + SkColor kStrokeColor = 0xFF657A91; + int kStrokeWidth = 1; + int kVerticalPadding = 8; + int kHorizontalPadding = 12; + int kCornerRadius = 4; + + go_incognito_button_ = + new TextButtonWithHandCursorOver(this, std::wstring()); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + go_incognito_button_->SetIcon(*rb.GetBitmapNamed(IDR_INCOGNITO_GUY)); + go_incognito_button_->SetFocusable(true); + // Set label colors. + go_incognito_button_->SetEnabledColor(SK_ColorWHITE); + go_incognito_button_->SetDisabledColor(SK_ColorWHITE); + go_incognito_button_->SetHighlightColor(SK_ColorWHITE); + go_incognito_button_->SetHoverColor(SK_ColorWHITE); + // Disable throbbing and make border always visible. + go_incognito_button_->SetAnimationDuration(0); + go_incognito_button_->SetNormalHasBorder(true); + // Setup round shapes. + go_incognito_button_->set_background( + CreateRoundedBackground( + kCornerRadius, kStrokeWidth, kButtonColor, kStrokeColor)); + + go_incognito_button_->set_border( + views::Border::CreateEmptyBorder(kVerticalPadding, + kHorizontalPadding, + kVerticalPadding, + kHorizontalPadding)); + // Set button text. + UpdateLocalizedStrings(); + // Enable and add to the views hierarchy. + go_incognito_button_->SetEnabled(true); + AddChildView(go_incognito_button_); +} + +void BackgroundView::UpdateLocalizedStrings() { + if (go_incognito_button_) { + go_incognito_button_->SetText( + UTF8ToWide(l10n_util::GetStringUTF8(IDS_GO_INCOGNITO_BUTTON))); + } +} + void BackgroundView::UpdateWindowType() { std::vector<int> params; params.push_back(did_paint_ ? 1 : 0); @@ -223,7 +445,7 @@ void BackgroundView::OnBootTimes( if (boot_times.chrome > 0) { boot_times_text = - StringPrintf( + base::StringPrintf( kBootTimesChromeExec, boot_times.total, boot_times.firmware, @@ -232,7 +454,7 @@ void BackgroundView::OnBootTimes( boot_times.chrome); } else { boot_times_text = - StringPrintf( + base::StringPrintf( kBootTimesNoChromeExec, boot_times.total, boot_times.firmware, diff --git a/chrome/browser/chromeos/login/background_view.h b/chrome/browser/chromeos/login/background_view.h index f5c4540..9fcad2a 100644 --- a/chrome/browser/chromeos/login/background_view.h +++ b/chrome/browser/chromeos/login/background_view.h @@ -4,45 +4,108 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_BACKGROUND_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_BACKGROUND_VIEW_H_ +#pragma once #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/cros/cros_library.h" -#include "chrome/browser/chromeos/version_loader.h" #include "chrome/browser/chromeos/status/status_area_host.h" +#include "chrome/browser/chromeos/version_loader.h" +#include "views/controls/button/button.h" #include "views/view.h" namespace views { class Widget; class Label; +class TextButton; } +class DOMView; +class GURL; class Profile; namespace chromeos { +class OobeProgressBar; class StatusAreaView; // View used to render the background during login. BackgroundView contains // StatusAreaView. -class BackgroundView : public views::View, public StatusAreaHost { +class BackgroundView : public views::View, + public StatusAreaHost, + public views::ButtonListener { public: + // Delegate class to handle notificatoin from the view. + class Delegate { + public: + virtual ~Delegate() {} + + // Initializes incognito login. + virtual void OnGoIncognitoButton() = 0; + }; + + enum LoginStep { + SELECT_NETWORK, +#if defined(OFFICIAL_BUILD) + EULA, +#endif + SIGNIN, +#if defined(OFFICIAL_BUILD) + REGISTRATION, +#endif + PICTURE + }; + BackgroundView(); + // Initializes the background view. It backgroun_url is given (non empty), + // it creates a DOMView background area that renders a webpage. + void Init(const GURL& background_url); + // Creates a window containing an instance of WizardContentsView as the root // view. The caller is responsible for showing (and closing) the returned - // widget. The BackgroundView is set in |view|. - static views::Widget* CreateWindowContainingView(const gfx::Rect& bounds, - BackgroundView** view); + // widget. The BackgroundView is set in |view|. If background_url is non + // empty, the content page of the url is displayed as a background. + static views::Widget* CreateWindowContainingView( + const gfx::Rect& bounds, + const GURL& background_url, + BackgroundView** view); // Toggles status area visibility. void SetStatusAreaVisible(bool visible); + // Toggles OOBE progress bar visibility, the bar is hidden by default. + void SetOobeProgressBarVisible(bool visible); + + // Gets progress bar visibility. + bool IsOobeProgressBarVisible(); + + // Sets current step on OOBE progress bar. + void SetOobeProgress(LoginStep step); + + // Toggles GoIncognito button visibility. + void SetGoIncognitoButtonVisible(bool visible, Delegate *delegate); + + // Shows screen saver. + void ShowScreenSaver(); + + // Hides screen saver. + void HideScreenSaver(); + + // Tells if screen saver is visible. + bool IsScreenSaverVisible(); + + // Tells if screen saver is enabled. + bool ScreenSaverEnabled(); + + // Tells that owner has been changed. + void OnOwnerChanged(); + protected: // Overridden from views::View: virtual void Paint(gfx::Canvas* canvas); virtual void Layout(); virtual void ChildPreferredSizeChanged(View* child); - virtual void LocaleChanged(); + virtual void OnLocaleChanged(); // Overridden from StatusAreaHost: virtual Profile* GetProfile() const { return NULL; } @@ -51,15 +114,24 @@ class BackgroundView : public views::View, public StatusAreaHost { virtual bool ShouldOpenButtonOptions( const views::View* button_view) const; virtual void OpenButtonOptions(const views::View* button_view) const; - virtual bool IsButtonVisible(const views::View* button_view) const; virtual bool IsBrowserMode() const; virtual bool IsScreenLockerMode() const; -private: + // Overridden from views::ButtonListener. + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + private: // Creates and adds the status_area. void InitStatusArea(); // Creates and adds the labels for version and boot time. void InitInfoLabels(); + // Creates and add OOBE progress bar. + void InitProgressBar(); + // Creates and add GoIncoginito button. + void InitGoIncognitoButton(); + + // Updates string from the resources. + void UpdateLocalizedStrings(); // Invokes SetWindowType for the window. This is invoked during startup and // after we've painted. @@ -71,9 +143,12 @@ private: void OnBootTimes( BootTimesLoader::Handle handle, BootTimesLoader::BootTimes boot_times); + // All of these variables could be NULL. StatusAreaView* status_area_; views::Label* os_version_label_; views::Label* boot_times_label_; + OobeProgressBar* progress_bar_; + views::TextButton* go_incognito_button_; // Handles asynchronously loading the version. VersionLoader version_loader_; @@ -90,6 +165,11 @@ private: // TODO(sky): nuke this when the wm knows when chrome has painted. bool did_paint_; + Delegate *delegate_; + + // DOMView for rendering a webpage as a background. + DOMView* background_area_; + DISALLOW_COPY_AND_ASSIGN(BackgroundView); }; diff --git a/chrome/browser/chromeos/login/camera.cc b/chrome/browser/chromeos/login/camera.cc index db0fde7..8e1bef6 100644 --- a/chrome/browser/chromeos/login/camera.cc +++ b/chrome/browser/chromeos/login/camera.cc @@ -21,6 +21,7 @@ #include "base/logging.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "gfx/size.h" #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -242,7 +243,7 @@ void Camera::StopCapturing() { int Camera::OpenDevice(const char* device_name) const { struct stat st; if (stat(device_name, &st) == -1) { - log_errno(StringPrintf("Cannot identify %s", device_name)); + log_errno(base::StringPrintf("Cannot identify %s", device_name)); return -1; } if (!S_ISCHR(st.st_mode)) { @@ -251,7 +252,7 @@ int Camera::OpenDevice(const char* device_name) const { } int fd = open(device_name, O_RDWR | O_NONBLOCK, 0); if (fd == -1) { - log_errno(StringPrintf("Cannot open %s", device_name)); + log_errno(base::StringPrintf("Cannot open %s", device_name)); return -1; } return fd; diff --git a/chrome/browser/chromeos/login/camera.h b/chrome/browser/chromeos/login/camera.h index 0acfecd..eb4498c 100644 --- a/chrome/browser/chromeos/login/camera.h +++ b/chrome/browser/chromeos/login/camera.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_CAMERA_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_CAMERA_H_ +#pragma once #include <string> #include <vector> diff --git a/chrome/browser/chromeos/login/captcha_view.h b/chrome/browser/chromeos/login/captcha_view.h index 872729a..491c438 100644 --- a/chrome/browser/chromeos/login/captcha_view.h +++ b/chrome/browser/chromeos/login/captcha_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_CAPTCHA_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_CAPTCHA_VIEW_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/client_login_response_handler.h b/chrome/browser/chromeos/login/client_login_response_handler.h index c0f1227..8fc1fe1 100644 --- a/chrome/browser/chromeos/login/client_login_response_handler.h +++ b/chrome/browser/chromeos/login/client_login_response_handler.h @@ -4,10 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_ +#pragma once #include <string> -#include "base/logging.h" +#include "base/basictypes.h" #include "chrome/browser/chromeos/login/auth_response_handler.h" class URLRequestContextGetter; diff --git a/chrome/browser/chromeos/login/cookie_fetcher.cc b/chrome/browser/chromeos/login/cookie_fetcher.cc index 48c7dec..0ea3c74 100644 --- a/chrome/browser/chromeos/login/cookie_fetcher.cc +++ b/chrome/browser/chromeos/login/cookie_fetcher.cc @@ -5,7 +5,6 @@ #include "chrome/browser/chromeos/login/cookie_fetcher.h" #include "base/command_line.h" -#include "base/file_path.h" #include "base/path_service.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/login/client_login_response_handler.h" diff --git a/chrome/browser/chromeos/login/cookie_fetcher.h b/chrome/browser/chromeos/login/cookie_fetcher.h index ef82d55..4c6d66a 100644 --- a/chrome/browser/chromeos/login/cookie_fetcher.h +++ b/chrome/browser/chromeos/login/cookie_fetcher.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_ +#pragma once #include <string> #include "base/scoped_ptr.h" diff --git a/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc index d068007..b5e3a4f 100644 --- a/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc +++ b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc @@ -5,16 +5,17 @@ #include <errno.h> #include <string> #include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/login/cookie_fetcher.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/client_login_response_handler.h" +#include "chrome/browser/chromeos/login/cookie_fetcher.h" #include "chrome/browser/chromeos/login/issue_response_handler.h" #include "chrome/browser/chromeos/login/mock_auth_response_handler.h" #include "chrome/common/net/url_fetcher.h" #include "chrome/test/testing_profile.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chromeos { using ::testing::Return; diff --git a/chrome/browser/chromeos/login/eula_view.cc b/chrome/browser/chromeos/login/eula_view.cc index 2efe93f..b9e7b2a 100644 --- a/chrome/browser/chromeos/login/eula_view.cc +++ b/chrome/browser/chromeos/login/eula_view.cc @@ -10,42 +10,55 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/basictypes.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/customization_document.h" +#include "chrome/browser/chromeos/login/help_app_launcher.h" #include "chrome/browser/chromeos/login/network_screen_delegate.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" -#include "chrome/browser/chromeos/login/screen_observer.h" +#include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/options_util.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/dom_view.h" +#include "chrome/common/native_web_keyboard_event.h" +#include "chrome/common/url_constants.h" #include "chrome/installer/util/google_update_settings.h" +#include "cros/chromeos_cryptohome.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "grit/locale_settings.h" #include "grit/theme_resources.h" #include "views/controls/button/checkbox.h" #include "views/controls/button/native_button.h" #include "views/controls/label.h" -#include "views/controls/textfield/textfield.h" #include "views/grid_layout.h" +#include "views/layout_manager.h" #include "views/standard_layout.h" +#include "views/widget/widget_gtk.h" +#include "views/window/dialog_delegate.h" +#include "views/window/window.h" + +#if defined(USE_LINUX_BREAKPAD) +#include "chrome/app/breakpad_linux.h" +#endif + +using views::WidgetGtk; namespace { const int kBorderSize = 10; const int kMargin = 20; const int kLastButtonHorizontalMargin = 10; -const int kTextMargin = 10; const int kCheckBowWidth = 22; +const int kTextMargin = 10; -// Fake EULA texts. TODO(glotov): implement reading actual file. -const wchar_t kLoremIpsum[] = L"Lorem ipsum dolor sit amet, " - L"consectetur adipisicing elit, sed do eiusmod tempor incididunt ut" - L"labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " - L"exercitation ullamco laboris nisi ut aliquip ex ea commodo " - L"consequat. Duis aute irure dolor in reprehenderit in voluptate velit " - L"esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat " - L"cupidatat non proident, sunt in culpa qui officia deserunt mollit anim " - L"id est laborum.\n"; -const wchar_t kFakeGoogleEula[] = L"\nGoogle Chrome Terms of Service\n" - L"These Terms of Service apply to the executable code version of " - L"Google Chrome. "; -const wchar_t kFakeOemEula[] = L"\nYBH Terms of Service\n"; +// TODO(glotov): this URL should be changed to actual Google ChromeOS EULA. +// See crbug.com/4647 +const char kGoogleEulaUrl[] = "about:terms"; enum kLayoutColumnsets { SINGLE_CONTROL_ROW, @@ -54,35 +67,106 @@ enum kLayoutColumnsets { LAST_ROW }; +// A simple LayoutManager that causes the associated view's one child to be +// sized to match the bounds of its parent except the bounds, if set. +struct FillLayoutWithBorder : public views::LayoutManager { + // Overridden from LayoutManager: + virtual void Layout(views::View* host) { + DCHECK(host->GetChildViewCount()); + host->GetChildViewAt(0)->SetBounds(host->GetLocalBounds(false)); + } + virtual gfx::Size GetPreferredSize(views::View* host) { + return gfx::Size(host->width(), host->height()); + } +}; + +// System security setting dialog. +class TpmInfoView : public views::View, + public views::DialogDelegate { + public: + explicit TpmInfoView(std::wstring password) : password_(password) { } + void Init(); + + protected: + // views::DialogDelegate overrides: + virtual bool Accept() { return true; } + virtual bool IsModal() const { return true; } + virtual views::View* GetContentsView() { return this; } + virtual int GetDialogButtons() const { + return MessageBoxFlags::DIALOGBUTTON_OK; + } + + // views::View overrides: + virtual std::wstring GetWindowTitle() const { + return l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING); + } + + gfx::Size GetPreferredSize() { + return gfx::Size(views::Window::GetLocalizedContentsSize( + IDS_TPM_INFO_DIALOG_WIDTH_CHARS, + IDS_TPM_INFO_DIALOG_HEIGHT_LINES)); + } + + private: + std::wstring password_; + DISALLOW_COPY_AND_ASSIGN(TpmInfoView); +}; + +void TpmInfoView::Init() { + views::GridLayout* layout = CreatePanelGridLayout(this); + SetLayoutManager(layout); + views::ColumnSet* column_set = layout->AddColumnSet(0); + column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + layout->StartRow(0, 0); + views::Label* label = new views::Label( + l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING_DESCRIPTION)); + label->SetMultiLine(true); + layout->AddView(label); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + column_set = layout->AddColumnSet(1); + column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + layout->StartRow(0, 1); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + gfx::Font password_font = + rb.GetFont(ResourceBundle::MediumFont).DeriveFont(0, gfx::Font::BOLD); + label = new views::Label(password_, password_font); + layout->AddView(label); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); +} + } // namespace namespace chromeos { +//////////////////////////////////////////////////////////////////////////////// +// EulaView, public: + EulaView::EulaView(chromeos::ScreenObserver* observer) - : google_eula_text_(NULL), + : google_eula_label_(NULL), + google_eula_view_(NULL), usage_statistics_checkbox_(NULL), learn_more_link_(NULL), - oem_eula_text_(NULL), + oem_eula_label_(NULL), + oem_eula_view_(NULL), system_security_settings_link_(NULL), - cancel_button_(NULL), + back_button_(NULL), continue_button_(NULL), - observer_(observer) { + observer_(observer), + bubble_(NULL) { } EulaView::~EulaView() { + // bubble_ will be set to NULL in callback. + if (bubble_) + bubble_->Close(); } -void EulaView::Init() { - // Use rounded rect background. - views::Painter* painter = CreateWizardPainter( - &BorderDefinition::kScreenBorder); - set_background( - views::Background::CreateBackgroundPainter(true, painter)); - - // Layout created controls. +// Convenience function to set layout's columnsets for this screen. +static void SetUpGridLayout(views::GridLayout* layout) { static const int kPadding = kBorderSize + kMargin; - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); views::ColumnSet* column_set = layout->AddColumnSet(SINGLE_CONTROL_ROW); column_set->AddPaddingColumn(0, kPadding); column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, @@ -111,21 +195,77 @@ void EulaView::Init() { column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); column_set->AddPaddingColumn(0, kLastButtonHorizontalMargin + kBorderSize); +} + +// Convenience function. Returns URL of the OEM EULA page that should be +// displayed using current locale and manifest. Returns empty URL otherwise. +static GURL GetOemEulaPagePath() { + const StartupCustomizationDocument *customization = + WizardController::default_controller()->GetCustomization(); + if (customization) { + std::string locale = g_browser_process->GetApplicationLocale(); + FilePath eula_page_path = customization->GetEULAPagePath(locale); + if (eula_page_path.empty()) { + LOG(INFO) << "No eula found for locale: " << locale; + locale = customization->initial_locale(); + eula_page_path = customization->GetEULAPagePath(locale); + } + if (!eula_page_path.empty()) { + const std::string page_path = std::string(chrome::kFileScheme) + + chrome::kStandardSchemeSeparator + eula_page_path.value(); + return GURL(page_path); + } else { + LOG(INFO) << "No eula found for locale: " << locale; + } + } else { + LOG(ERROR) << "No manifest found."; + } + return GURL(); +} + +void EulaView::Init() { + // Use rounded rect background. + views::Painter* painter = CreateWizardPainter( + &BorderDefinition::kScreenBorder); + set_background( + views::Background::CreateBackgroundPainter(true, painter)); + // Layout created controls. + views::GridLayout* layout = new views::GridLayout(this); + SetLayoutManager(layout); + SetUpGridLayout(layout); + + static const int kPadding = kBorderSize + kMargin; layout->AddPaddingRow(0, kPadding); + layout->StartRow(0, SINGLE_CONTROL_ROW); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + gfx::Font label_font = + rb.GetFont(ResourceBundle::MediumFont).DeriveFont(0, gfx::Font::NORMAL); + google_eula_label_ = new views::Label(std::wstring(), label_font); + layout->AddView(google_eula_label_, 1, 1, + views::GridLayout::LEADING, views::GridLayout::FILL); + + layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); layout->StartRow(1, SINGLE_CONTROL_ROW); - google_eula_text_ = new views::Textfield(views::Textfield::STYLE_MULTILINE); - google_eula_text_->SetReadOnly(true); - google_eula_text_->SetFocusable(true); - google_eula_text_->SetHorizontalMargins(kTextMargin, kTextMargin); - layout->AddView(google_eula_text_); + views::View* box_view = new views::View(); + box_view->set_border(views::Border::CreateSolidBorder(1, SK_ColorBLACK)); + box_view->SetLayoutManager(new FillLayoutWithBorder()); + layout->AddView(box_view); + google_eula_view_ = new DOMView(); + box_view->AddChildView(google_eula_view_); + + layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); layout->StartRow(0, SINGLE_CONTROL_WITH_SHIFT_ROW); usage_statistics_checkbox_ = new views::Checkbox(); usage_statistics_checkbox_->SetMultiLine(true); - usage_statistics_checkbox_->SetChecked( - GoogleUpdateSettings::GetCollectStatsConsent()); + + // TODO(zelidrag): http://crosbug/6367 - Change default settings for checked + // and enabled state of usage_statistics_checkbox_ before the product is + // released. + usage_statistics_checkbox_->SetChecked(true); layout->AddView(usage_statistics_checkbox_); + usage_statistics_checkbox_->SetEnabled(false); layout->StartRow(0, SINGLE_LINK_WITH_SHIFT_ROW); learn_more_link_ = new views::Link(); @@ -133,22 +273,37 @@ void EulaView::Init() { layout->AddView(learn_more_link_); layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); - layout->StartRow(1, SINGLE_CONTROL_ROW); - oem_eula_text_ = new views::Textfield(views::Textfield::STYLE_MULTILINE); - oem_eula_text_->SetReadOnly(true); - oem_eula_text_->SetFocusable(true); - oem_eula_text_->SetHorizontalMargins(kTextMargin, kTextMargin); - layout->AddView(oem_eula_text_); + layout->StartRow(0, SINGLE_CONTROL_ROW); + oem_eula_label_ = new views::Label(std::wstring(), label_font); + layout->AddView(oem_eula_label_, 1, 1, + views::GridLayout::LEADING, views::GridLayout::FILL); + + oem_eula_page_ = GetOemEulaPagePath(); + if (!oem_eula_page_.is_empty()) { + layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); + layout->StartRow(1, SINGLE_CONTROL_ROW); + box_view = new views::View(); + box_view->SetLayoutManager(new FillLayoutWithBorder()); + box_view->set_border(views::Border::CreateSolidBorder(1, SK_ColorBLACK)); + layout->AddView(box_view); + + oem_eula_view_ = new DOMView(); + box_view->AddChildView(oem_eula_view_); + } - layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, LAST_ROW); system_security_settings_link_ = new views::Link(); system_security_settings_link_->SetController(this); + if (!chromeos::CrosLibrary::Get()->EnsureLoaded() || + !chromeos::CryptohomeTpmIsEnabled()) { + system_security_settings_link_->SetEnabled(false); + // TODO(glotov): add tooltip with description. + } layout->AddView(system_security_settings_link_); - cancel_button_ = new views::NativeButton(this, std::wstring()); - cancel_button_->SetEnabled(false); - layout->AddView(cancel_button_); + back_button_ = new views::NativeButton(this, std::wstring()); + layout->AddView(back_button_); continue_button_ = new views::NativeButton(this, std::wstring()); layout->AddView(continue_button_); @@ -158,28 +313,36 @@ void EulaView::Init() { } void EulaView::UpdateLocalizedStrings() { - google_eula_text_->SetText(WideToUTF16(kFakeGoogleEula) + - WideToUTF16(kLoremIpsum) + - WideToUTF16(kLoremIpsum)); - oem_eula_text_->SetText(WideToUTF16(kFakeOemEula) + - WideToUTF16(kLoremIpsum) + - WideToUTF16(kLoremIpsum)); + // Load Google EULA and its title. + LoadEulaView(google_eula_view_, google_eula_label_, GURL(kGoogleEulaUrl)); + + // Load OEM EULA and its title. + if (!oem_eula_page_.is_empty()) + LoadEulaView(oem_eula_view_, oem_eula_label_, oem_eula_page_); + + // Set tooltip for usage statistics checkbox if the metric is unmanaged. + if (!usage_statistics_checkbox_->IsEnabled()) { + usage_statistics_checkbox_->SetTooltipText( + l10n_util::GetString(IDS_OPTION_DISABLED_BY_POLICY)); + } + + // Load other labels from resources. usage_statistics_checkbox_->SetLabel( l10n_util::GetString(IDS_EULA_CHECKBOX_ENABLE_LOGGING)); learn_more_link_->SetText( l10n_util::GetString(IDS_LEARN_MORE)); system_security_settings_link_->SetText( - l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTINGS_LINK)); + l10n_util::GetString(IDS_EULA_SYSTEM_SECURITY_SETTING)); continue_button_->SetLabel( l10n_util::GetString(IDS_EULA_ACCEPT_AND_CONTINUE_BUTTON)); - cancel_button_->SetLabel( - l10n_util::GetString(IDS_CANCEL)); + back_button_->SetLabel( + l10n_util::GetString(IDS_ACCNAME_BACK)); } //////////////////////////////////////////////////////////////////////////////// -// views::View: implementation: +// EulaView, protected, views::View implementation: -void EulaView::LocaleChanged() { +void EulaView::OnLocaleChanged() { UpdateLocalizedStrings(); Layout(); } @@ -190,19 +353,114 @@ void EulaView::LocaleChanged() { void EulaView::ButtonPressed(views::Button* sender, const views::Event& event) { if (sender == continue_button_) { if (usage_statistics_checkbox_) { - GoogleUpdateSettings::SetCollectStatsConsent( - usage_statistics_checkbox_->checked()); + bool enable_reporting = usage_statistics_checkbox_->checked(); + enable_reporting = + OptionsUtil::ResolveMetricsReportingEnabled(enable_reporting); +#if defined(USE_LINUX_BREAKPAD) + if (enable_reporting) + InitCrashReporter(); +#endif } observer_->OnExit(ScreenObserver::EULA_ACCEPTED); + } else if (sender == back_button_) { + observer_->OnExit(ScreenObserver::EULA_BACK); } - // TODO(glotov): handle cancel button. } //////////////////////////////////////////////////////////////////////////////// // views::LinkController implementation: void EulaView::LinkActivated(views::Link* source, int event_flags) { - // TODO(glotov): handle link clicks. + if (source == learn_more_link_) { + if (!help_app_.get()) + help_app_.reset(new HelpAppLauncher(GetNativeWindow())); + help_app_->ShowHelpTopic(HelpAppLauncher::HELP_STATS_USAGE); + } else if (source == system_security_settings_link_) { + // Pull the password from TPM. + std::string password; + if (!chromeos::CrosLibrary::Get()->EnsureLoaded()) { + LOG(ERROR) << "Cros library not loaded. " + << "We must have disabled the link that led here."; + return; + } else if (chromeos::CryptohomeTpmIsReady() && + chromeos::CryptohomeTpmGetPassword(&password)) { + TpmInfoView* view = new TpmInfoView(ASCIIToWide(password)); + view->Init(); + views::Window* window = views::Window::CreateChromeWindow( + GetNativeWindow(), gfx::Rect(), view); + window->SetIsAlwaysOnTop(true); + window->Show(); + } else { + if (!bubble_) + bubble_ = MessageBubble::Show( + system_security_settings_link_->GetWidget(), + system_security_settings_link_->GetScreenBounds(), + BubbleBorder::LEFT_TOP, + ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), + l10n_util::GetString(IDS_EULA_TPM_BUSY), + std::wstring(), this); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TabContentsDelegate implementation: + +// Convenience function. Queries |eula_view| for HTML title and, if it +// is ready, assigns it to |eula_label| and returns true so the caller +// view calls Layout(). +static bool PublishTitleIfReady(const TabContents* contents, + DOMView* eula_view, + views::Label* eula_label) { + if (contents != eula_view->tab_contents()) + return false; + eula_label->SetText(UTF16ToWide(eula_view->tab_contents()->GetTitle())); + return true; +} + +void EulaView::NavigationStateChanged(const TabContents* contents, + unsigned changed_flags) { + if (changed_flags & TabContents::INVALIDATE_TITLE) { + if (PublishTitleIfReady(contents, google_eula_view_, google_eula_label_) || + PublishTitleIfReady(contents, oem_eula_view_, oem_eula_label_)) { + Layout(); + } + } +} + +void EulaView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { + views::Widget* widget = GetWidget(); + if (widget && event.os_event && !event.skip_in_browser) + static_cast<views::WidgetGtk*>(widget)->HandleKeyboardEvent(event.os_event); +} + +//////////////////////////////////////////////////////////////////////////////// +// EulaView, private: + +gfx::NativeWindow EulaView::GetNativeWindow() const { + return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); +} + +void EulaView::LoadEulaView(DOMView* eula_view, + views::Label* eula_label, + const GURL& eula_url) { + Profile* profile = ProfileManager::GetDefaultProfile(); + eula_view->Init(profile, + SiteInstance::CreateSiteInstanceForURL(profile, eula_url)); + eula_view->LoadURL(eula_url); + eula_view->tab_contents()->set_delegate(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// EulaView, private, views::View implementation: + +bool EulaView::OnKeyPressed(const views::KeyEvent&) { + // Close message bubble if shown. bubble_ will be set to NULL in callback. + if (bubble_) { + bubble_->Close(); + return true; + } + return false; } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/eula_view.h b/chrome/browser/chromeos/login/eula_view.h index 9e7c112..ca40400 100644 --- a/chrome/browser/chromeos/login/eula_view.h +++ b/chrome/browser/chromeos/login/eula_view.h @@ -4,10 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_EULA_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_EULA_VIEW_H_ +#pragma once -#include <string> - +#include "base/scoped_ptr.h" +#include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/view_screen.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "gfx/native_widget_types.h" #include "views/controls/button/button.h" #include "views/controls/link.h" #include "views/view.h" @@ -18,16 +21,63 @@ class Checkbox; class Label; class Link; class NativeButton; -class Textfield; } // namespace views +class DOMView; + namespace chromeos { +class HelpAppLauncher; + +// Delegate for TabContents that will show EULA. +// Blocks context menu and other actions. +class EULATabContentsDelegate : public TabContentsDelegate { + public: + EULATabContentsDelegate() {} + virtual ~EULATabContentsDelegate() {} + + protected: + // TabContentsDelegate implementation: + virtual void OpenURLFromTab(TabContents* source, + const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition) {} + virtual void NavigationStateChanged(const TabContents* source, + unsigned changed_flags) {} + virtual void AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture) {} + virtual void ActivateContents(TabContents* contents) {} + virtual void DeactivateContents(TabContents* contents) {} + virtual void LoadingStateChanged(TabContents* source) {} + virtual void CloseContents(TabContents* source) {} + virtual bool IsPopup(TabContents* source) { return false; } + virtual void URLStarredChanged(TabContents* source, bool starred) {} + virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} + virtual bool ShouldAddNavigationToHistory( + const history::HistoryAddPageArgs& add_page_args, + NavigationType::Type navigation_type) { + return false; + } + virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} + virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} + virtual bool HandleContextMenu(const ContextMenuParams& params) { + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(EULATabContentsDelegate); +}; + class EulaView : public views::View, public views::ButtonListener, - public views::LinkController { + public views::LinkController, + public MessageBubbleDelegate, + public EULATabContentsDelegate { public: explicit EulaView(chromeos::ScreenObserver* observer); virtual ~EulaView(); @@ -40,7 +90,7 @@ class EulaView protected: // views::View implementation. - virtual void LocaleChanged(); + virtual void OnLocaleChanged(); // views::ButtonListener implementation. virtual void ButtonPressed(views::Button* sender, const views::Event& event); @@ -49,17 +99,55 @@ class EulaView void LinkActivated(views::Link* source, int event_flags); private: + // views::View implementation. + virtual bool SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { + return true; } + virtual bool OnKeyPressed(const views::KeyEvent& e); + + // TabContentsDelegate implementation. + virtual void NavigationStateChanged(const TabContents* contents, + unsigned changed_flags); + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); + + // Returns corresponding native window. + gfx::NativeWindow GetNativeWindow() const; + + // Loads specified URL to the specified DOMView and updates specified + // label with its title. + void LoadEulaView(DOMView* eula_view, + views::Label* eula_label, + const GURL& eula_url); + + // Overridden from views::InfoBubbleDelegate. + virtual void InfoBubbleClosing(InfoBubble* info_bubble, + bool closed_by_escape) { bubble_ = NULL; } + virtual bool CloseOnEscape() { return true; } + virtual bool FadeInOnShow() { return false; } + virtual void OnHelpLinkActivated() {} + // Dialog controls. - views::Textfield* google_eula_text_; + views::Label* google_eula_label_; + DOMView* google_eula_view_; views::Checkbox* usage_statistics_checkbox_; views::Link* learn_more_link_; - views::Textfield* oem_eula_text_; + views::Label* oem_eula_label_; + DOMView* oem_eula_view_; views::Link* system_security_settings_link_; - views::NativeButton* cancel_button_; + views::NativeButton* back_button_; views::NativeButton* continue_button_; chromeos::ScreenObserver* observer_; + // URL of the OEM EULA page (on disk). + GURL oem_eula_page_; + + // Help application used for help dialogs. + scoped_ptr<HelpAppLauncher> help_app_; + + // Pointer to shown message bubble. We don't need to delete it because + // it will be deleted on bubble closing. + MessageBubble* bubble_; + DISALLOW_COPY_AND_ASSIGN(EulaView); }; diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 5e6abeb..e8a609d 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -10,25 +10,27 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/command_line.h" #include "base/message_loop.h" #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chrome_thread.h" +#include "base/values.h" #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/cros/network_library.h" -#include "chrome/browser/chromeos/login/authenticator.h" +#include "chrome/browser/chromeos/cros_settings_provider_user.h" #include "chrome/browser/chromeos/login/background_view.h" +#include "chrome/browser/chromeos/login/help_app_launcher.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/wm_ipc.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/profile_manager.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" #include "gfx/native_widget_types.h" +#include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/screen.h" #include "views/widget/widget_gtk.h" @@ -40,20 +42,14 @@ namespace { // Max number of users we'll show. The true max is the min of this and the // number of windows that fit on the screen. -const size_t kMaxUsers = 6; +const size_t kMaxUsers = 5; // Used to indicate no user has been selected. const size_t kNotSelected = -1; -// ClientLogin response parameters. -const char kError[] = "Error="; -const char kCaptchaError[] = "CaptchaRequired"; -const char kCaptchaUrlParam[] = "CaptchaUrl="; -const char kCaptchaTokenParam[] = "CaptchaToken="; -const char kParamSuffix[] = "\n"; - -// URL prefix for CAPTCHA image. -const char kCaptchaUrlPrefix[] = "http://www.google.com/accounts/"; +// Offset of cursor in first position from edit left side. It's used to position +// info bubble arrow to cursor. +const int kCursorOffset = 5; // Checks if display names are unique. If there are duplicates, enables // tooltips with full emails to let users distinguish their accounts. @@ -65,14 +61,30 @@ void EnableTooltipsIfNeeded(const std::vector<UserController*>& controllers) { controllers[i]->user().GetDisplayName(); ++visible_display_names[display_name]; } - for (size_t i = 0; i + 1 < controllers.size(); ++i) { + for (size_t i = 0; i < controllers.size(); ++i) { const std::string& display_name = controllers[i]->user().GetDisplayName(); - bool show_tooltip = visible_display_names[display_name] > 1; + bool show_tooltip = controllers[i]->is_new_user() || + controllers[i]->is_bwsi() || + visible_display_names[display_name] > 1; controllers[i]->EnableNameTooltip(show_tooltip); } } +// Returns true if given email is in user whitelist. +// Note this function is for display purpose only and should use +// CheckWhitelist op for the real whitelist check. +bool IsEmailInCachedWhitelist(const std::string& email) { + StringValue email_value(email); + const ListValue* whitelist = UserCrosSettingsProvider::cached_whitelist(); + for (ListValue::const_iterator i(whitelist->begin()); + i != whitelist->end(); ++i) { + if ((*i)->Equals(&email_value)) + return true; + } + return false; +} + } // namespace ExistingUserController* @@ -85,38 +97,66 @@ ExistingUserController::ExistingUserController( background_window_(NULL), background_view_(NULL), selected_view_index_(kNotSelected), + num_login_attempts_(0), bubble_(NULL) { if (delete_scheduled_instance_) delete_scheduled_instance_->Delete(); // Caclulate the max number of users from available screen size. - size_t max_users = kMaxUsers; - int screen_width = background_bounds.width(); - if (screen_width > 0) { - max_users = std::max(static_cast<size_t>(2), std::min(kMaxUsers, - static_cast<size_t>((screen_width - login::kUserImageSize) - / (UserController::kUnselectedSize + - UserController::kPadding)))); + if (UserCrosSettingsProvider::cached_show_users_on_signin()) { + size_t max_users = kMaxUsers; + int screen_width = background_bounds.width(); + if (screen_width > 0) { + max_users = std::max(static_cast<size_t>(2), std::min(kMaxUsers, + static_cast<size_t>((screen_width - login::kUserImageSize) + / (UserController::kUnselectedSize + + UserController::kPadding)))); + } + + size_t visible_users_count = std::min(users.size(), max_users - 1); + for (size_t i = 0; i < users.size(); ++i) { + if (controllers_.size() == visible_users_count) + break; + + // TODO(xiyuan): Clean user profile whose email is not in whitelist. + if (UserCrosSettingsProvider::cached_allow_guest() || + IsEmailInCachedWhitelist(users[i].email())) { + controllers_.push_back(new UserController(this, users[i])); + } + } } - size_t visible_users_count = std::min(users.size(), max_users - 1); - for (size_t i = 0; i < visible_users_count; ++i) - controllers_.push_back(new UserController(this, users[i])); + if (!controllers_.empty() && UserCrosSettingsProvider::cached_allow_bwsi()) + controllers_.push_back(new UserController(this, true)); - // Add the view representing the guest user last. - controllers_.push_back(new UserController(this)); + // Add the view representing the new user. + controllers_.push_back(new UserController(this, false)); } void ExistingUserController::Init() { if (!background_window_) { + std::string url_string = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kScreenSaverUrl); + background_window_ = BackgroundView::CreateWindowContainingView( background_bounds_, + GURL(url_string), &background_view_); + + if (!WizardController::IsDeviceRegistered()) { + background_view_->SetOobeProgressBarVisible(true); + background_view_->SetOobeProgress(chromeos::BackgroundView::SIGNIN); + } + background_window_->Show(); } + // If there's only new user pod, show BWSI link on it. + bool show_bwsi_link = controllers_.size() == 1; for (size_t i = 0; i < controllers_.size(); ++i) { (controllers_[i])->Init(static_cast<int>(i), - static_cast<int>(controllers_.size())); + static_cast<int>(controllers_.size()), + show_bwsi_link); } EnableTooltipsIfNeeded(controllers_); @@ -133,6 +173,28 @@ void ExistingUserController::OwnBackground( DCHECK(!background_window_); background_window_ = background_widget; background_view_ = background_view; + background_view_->OnOwnerChanged(); +} + +void ExistingUserController::LoginNewUser(const std::string& username, + const std::string& password) { + SelectNewUser(); + UserController* new_user = controllers_.back(); + DCHECK(new_user->is_new_user()); + if (!new_user->is_new_user()) + return; + NewUserView* new_user_view = new_user->new_user_view(); + new_user_view->SetUsername(username); + + if (password.empty()) + return; + + new_user_view->SetPassword(password); + new_user_view->Login(); +} + +void ExistingUserController::SelectNewUser() { + SelectUser(controllers_.size() - 1); } ExistingUserController::~ExistingUserController() { @@ -171,33 +233,47 @@ void ExistingUserController::Login(UserController* source, std::vector<UserController*>::const_iterator i = std::find(controllers_.begin(), controllers_.end(), source); DCHECK(i != controllers_.end()); - selected_view_index_ = i - controllers_.begin(); - - authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); - Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile(); - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(authenticator_.get(), - &Authenticator::AuthenticateToLogin, - profile, - controllers_[selected_view_index_]->user().email(), - UTF16ToUTF8(password), - login_token_, - login_captcha_)); + + if (i == controllers_.begin() + selected_view_index_) { + num_login_attempts_++; + } else { + selected_view_index_ = i - controllers_.begin(); + num_login_attempts_ = 0; + } // Disable clicking on other windows. SendSetLoginState(false); + + // Use the same LoginPerformer for subsequent login as it has state + // such as CAPTCHA challenge token & corresponding user input. + if (!login_performer_.get() || num_login_attempts_ <= 1) { + login_performer_.reset(new LoginPerformer(this)); + } + login_performer_->Login(controllers_[selected_view_index_]->user().email(), + UTF16ToUTF8(password)); +} + +void ExistingUserController::WhiteListCheckFailed(const std::string& email) { + ShowError(IDS_LOGIN_ERROR_WHITELIST, email); + + // Reenable userview and use ClearAndEnablePassword to keep username on + // screen with the error bubble. + controllers_[selected_view_index_]->ClearAndEnablePassword(); + + // Reenable clicking on other windows. + SendSetLoginState(true); } void ExistingUserController::LoginOffTheRecord() { - authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(authenticator_.get(), - &Authenticator::LoginOffTheRecord)); + // Check allow_bwsi in case this call is fired from key accelerator. + if (!UserCrosSettingsProvider::cached_allow_bwsi()) + return; // Disable clicking on other windows. SendSetLoginState(false); + + login_performer_.reset(new LoginPerformer(this)); + login_performer_->LoginOffTheRecord(); } void ExistingUserController::ClearErrors() { @@ -213,8 +289,9 @@ void ExistingUserController::OnUserSelected(UserController* source) { size_t new_selected_index = i - controllers_.begin(); if (new_selected_index != selected_view_index_ && selected_view_index_ != kNotSelected) { - controllers_[selected_view_index_]->ClearAndEnablePassword(); - ClearCaptchaState(); + controllers_[selected_view_index_]->ClearAndEnableFields(); + login_performer_.reset(NULL); + num_login_attempts_ = 0; } selected_view_index_ = new_selected_index; } @@ -222,13 +299,15 @@ void ExistingUserController::OnUserSelected(UserController* source) { void ExistingUserController::ActivateWizard(const std::string& screen_name) { // WizardController takes care of deleting itself when done. WizardController* controller = new WizardController(); - controller->Init(screen_name, background_bounds_); - controller->Show(); // Give the background window to the controller. controller->OwnBackground(background_window_, background_view_); background_window_ = NULL; + controller->Init(screen_name, background_bounds_); + controller->set_start_url(start_url_); + controller->Show(); + // And schedule us for deletion. We delay for a second as the window manager // is doing an animation with our windows. DCHECK(!delete_scheduled_instance_); @@ -242,14 +321,18 @@ void ExistingUserController::RemoveUser(UserController* source) { UserManager::Get()->RemoveUser(source->user().email()); - // We need to unmap entry windows, the windows will be unmapped in destructor. controllers_.erase(controllers_.begin() + source->user_index()); - delete source; EnableTooltipsIfNeeded(controllers_); + + // Update user count before unmapping windows, otherwise window manager won't + // be in the right state. int new_size = static_cast<int>(controllers_.size()); for (int i = 0; i < new_size; ++i) controllers_[i]->UpdateUserCount(i, new_size); + + // We need to unmap entry windows, the windows will be unmapped in destructor. + delete source; } void ExistingUserController::SelectUser(int index) { @@ -261,9 +344,12 @@ void ExistingUserController::SelectUser(int index) { } } -void ExistingUserController::OnLoginFailure(const std::string& error) { - LOG(INFO) << "OnLoginFailure"; - ClearCaptchaState(); +void ExistingUserController::OnGoIncognitoButton() { + LoginOffTheRecord(); +} + +void ExistingUserController::OnLoginFailure(const LoginFailure& failure) { + std::string error = failure.GetErrorString(); // Check networking after trying to login in case user is // cached locally or the local admin account. @@ -273,28 +359,25 @@ void ExistingUserController::OnLoginFailure(const std::string& error) { } else if (!network->Connected()) { ShowError(IDS_LOGIN_ERROR_OFFLINE_FAILED_NETWORK_NOT_CONNECTED, error); } else { - std::string error_code = LoginUtils::ExtractClientLoginParam(error, - kError, - kParamSuffix); - std::string captcha_url; - if (error_code == kCaptchaError) - captcha_url = LoginUtils::ExtractClientLoginParam(error, - kCaptchaUrlParam, - kParamSuffix); - if (captcha_url.empty()) { - ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error); + if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED && + failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) { + if (!failure.error().captcha().image_url.is_empty()) { + CaptchaView* view = + new CaptchaView(failure.error().captcha().image_url); + view->set_delegate(this); + views::Window* window = views::Window::CreateChromeWindow( + GetNativeWindow(), gfx::Rect(), view); + window->SetIsAlwaysOnTop(true); + window->Show(); + } else { + LOG(WARNING) << "No captcha image url was found?"; + ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error); + } } else { - // Save token for next login retry. - login_token_ = LoginUtils::ExtractClientLoginParam(error, - kCaptchaTokenParam, - kParamSuffix); - CaptchaView* view = - new CaptchaView(GURL(kCaptchaUrlPrefix + captcha_url)); - view->set_delegate(this); - views::Window* window = views::Window::CreateChromeWindow( - GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); + if (controllers_[selected_view_index_]->is_new_user()) + ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error); + else + ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error); } } @@ -305,15 +388,8 @@ void ExistingUserController::OnLoginFailure(const std::string& error) { } void ExistingUserController::AppendStartUrlToCmdline() { - if (start_url_.is_valid()) { - CommandLine::ForCurrentProcess()->AppendLooseValue( - UTF8ToWide(start_url_.spec())); - } -} - -void ExistingUserController::ClearCaptchaState() { - login_token_.clear(); - login_captcha_.clear(); + if (start_url_.is_valid()) + CommandLine::ForCurrentProcess()->AppendArg(start_url_.spec()); } gfx::NativeWindow ExistingUserController::GetNativeWindow() const { @@ -325,14 +401,31 @@ void ExistingUserController::ShowError(int error_id, const std::string& details) { ClearErrors(); std::wstring error_text = l10n_util::GetString(error_id); - if (!details.empty()) - error_text += L"\n" + ASCIIToWide(details); + // TODO(dpolukhin): show detailed error info. |details| string contains + // low level error info that is not localized and even is not user friendly. + // For now just ignore it because error_text contains all required information + // for end users, developers can see details string in Chrome logs. + + gfx::Rect bounds = controllers_[selected_view_index_]->GetScreenBounds(); + BubbleBorder::ArrowLocation arrow; + if (controllers_[selected_view_index_]->is_new_user()) { + arrow = BubbleBorder::LEFT_TOP; + } else { + // Point info bubble arrow to cursor position (approximately). + bounds.set_width(kCursorOffset * 2); + arrow = BubbleBorder::BOTTOM_LEFT; + } + std::wstring help_link; + if (num_login_attempts_ > static_cast<size_t>(1)) + help_link = l10n_util::GetString(IDS_CANT_ACCESS_ACCOUNT_BUTTON); + bubble_ = MessageBubble::Show( controllers_[selected_view_index_]->controls_window(), - controllers_[selected_view_index_]->GetScreenBounds(), - BubbleBorder::BOTTOM_LEFT, + bounds, + arrow, ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), error_text, + help_link, this); } @@ -340,11 +433,14 @@ void ExistingUserController::OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& credentials) { AppendStartUrlToCmdline(); - if (selected_view_index_ + 1 == controllers_.size()) { + if (selected_view_index_ + 1 == controllers_.size() && + !UserManager::Get()->IsKnownUser(username)) { // For new user login don't launch browser until we pass image screen. - chromeos::LoginUtils::Get()->EnableBrowserLaunch(false); + LoginUtils::Get()->EnableBrowserLaunch(false); LoginUtils::Get()->CompleteLogin(username, credentials); - ActivateWizard(WizardController::kUserImageScreenName); + ActivateWizard(WizardController::IsDeviceRegistered() ? + WizardController::kUserImageScreenName : + WizardController::kRegistrationScreenName); } else { // Hide the login windows now. WmIpc::Message message(WM_IPC_MESSAGE_WM_HIDE_LOGIN); @@ -358,23 +454,22 @@ void ExistingUserController::OnLoginSuccess(const std::string& username, } void ExistingUserController::OnOffTheRecordLoginSuccess() { - AppendStartUrlToCmdline(); - LoginUtils::Get()->CompleteOffTheRecordLogin(); + if (WizardController::IsDeviceRegistered()) { + LoginUtils::Get()->CompleteOffTheRecordLogin(start_url_); + } else { + // Postpone CompleteOffTheRecordLogin until registration completion. + ActivateWizard(WizardController::kRegistrationScreenName); + } } void ExistingUserController::OnPasswordChangeDetected( const GaiaAuthConsumer::ClientLoginResult& credentials) { // When signing in as a "New user" always remove old cryptohome. if (selected_view_index_ == controllers_.size() - 1) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(authenticator_.get(), - &Authenticator::ResyncEncryptedData, - credentials)); + login_performer_->ResyncEncryptedData(); return; } - cached_credentials_ = credentials; PasswordChangedView* view = new PasswordChangedView(this); views::Window* window = views::Window::CreateChromeWindow(GetNativeWindow(), gfx::Rect(), @@ -383,28 +478,38 @@ void ExistingUserController::OnPasswordChangeDetected( window->Show(); } +void ExistingUserController::OnHelpLinkActivated() { + DCHECK(login_performer_->error().state() != GoogleServiceAuthError::NONE); + if (!help_app_.get()) + help_app_.reset(new HelpAppLauncher(GetNativeWindow())); + switch (login_performer_->error().state()) { + case(GoogleServiceAuthError::CONNECTION_FAILED): + help_app_->ShowHelpTopic( + HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE); + break; + case(GoogleServiceAuthError::ACCOUNT_DISABLED): + help_app_->ShowHelpTopic( + HelpAppLauncher::HELP_ACCOUNT_DISABLED); + break; + default: + help_app_->ShowHelpTopic(login_performer_->login_timed_out() ? + HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE : + HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); + break; + } +} + void ExistingUserController::OnCaptchaEntered(const std::string& captcha) { - login_captcha_ = captcha; + login_performer_->set_captcha(captcha); } void ExistingUserController::RecoverEncryptedData( const std::string& old_password) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(authenticator_.get(), - &Authenticator::RecoverEncryptedData, - old_password, - cached_credentials_)); - cached_credentials_ = GaiaAuthConsumer::ClientLoginResult(); + login_performer_->RecoverEncryptedData(old_password); } void ExistingUserController::ResyncEncryptedData() { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(authenticator_.get(), - &Authenticator::ResyncEncryptedData, - cached_credentials_)); - cached_credentials_ = GaiaAuthConsumer::ClientLoginResult(); + login_performer_->ResyncEncryptedData(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 856f7ca..6d97740 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -4,34 +4,33 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_EXISTING_USER_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_EXISTING_USER_CONTROLLER_H_ +#pragma once #include <string> #include <vector> -#include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "base/task.h" #include "base/timer.h" +#include "chrome/browser/chromeos/login/background_view.h" #include "chrome/browser/chromeos/login/captcha_view.h" -#include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/browser/chromeos/login/login_performer.h" +#include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/password_changed_view.h" -#include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/user_controller.h" +#include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/wm_message_listener.h" -#include "chrome/browser/views/info_bubble.h" -#include "chrome/common/net/gaia/gaia_auth_consumer.h" #include "gfx/size.h" namespace chromeos { -class Authenticator; -class BackgroundView; +class HelpAppLauncher; class MessageBubble; // ExistingUserController is used to handle login when someone has already // logged into the machine. When Init is invoked a UserController is created for -// each of the Users's in the UserManager (including one for guest), and the -// window manager is then told to show the windows. If the user clicks on the -// guest entry the WizardWindow is swapped in. +// each of the Users's in the UserManager (including one for new user and +// one for BWSI login), and the window manager is then told to show the windows. // // To use ExistingUserController create an instance of it and invoke Init. // @@ -39,8 +38,9 @@ class MessageBubble; // the user logs in (or chooses to see other settings). class ExistingUserController : public WmMessageListener::Observer, public UserController::Delegate, - public LoginStatusConsumer, - public InfoBubbleDelegate, + public BackgroundView::Delegate, + public LoginPerformer::Delegate, + public MessageBubbleDelegate, public CaptchaView::Delegate, public PasswordChangedView::Delegate { public: @@ -56,6 +56,13 @@ class ExistingUserController : public WmMessageListener::Observer, void OwnBackground(views::Widget* background_widget, chromeos::BackgroundView* background_view); + // Tries to login from new user pod with given user login and password. + // Called after creating new account. + void LoginNewUser(const std::string& username, const std::string& password); + + // Selects new user pod. + void SelectNewUser(); + private: friend class DeleteTask<ExistingUserController>; @@ -78,13 +85,17 @@ class ExistingUserController : public WmMessageListener::Observer, virtual void AddStartUrl(const GURL& start_url) { start_url_ = start_url; } virtual void SelectUser(int index); - // LoginStatusConsumer: - virtual void OnLoginFailure(const std::string& error); + // BackgroundView::Delegate + virtual void OnGoIncognitoButton(); + + // LoginPerformer::Delegate implementation: + virtual void OnLoginFailure(const LoginFailure& error); virtual void OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& credentials); virtual void OnOffTheRecordLoginSuccess(); virtual void OnPasswordChangeDetected( const GaiaAuthConsumer::ClientLoginResult& credentials); + virtual void WhiteListCheckFailed(const std::string& email); // Overridden from views::InfoBubbleDelegate. virtual void InfoBubbleClosing(InfoBubble* info_bubble, @@ -93,6 +104,7 @@ class ExistingUserController : public WmMessageListener::Observer, } virtual bool CloseOnEscape() { return true; } virtual bool FadeInOnShow() { return false; } + virtual void OnHelpLinkActivated(); // CaptchaView::Delegate: virtual void OnCaptchaEntered(const std::string& captcha); @@ -104,9 +116,6 @@ class ExistingUserController : public WmMessageListener::Observer, // Adds start url to command line. void AppendStartUrlToCmdline(); - // Clears existing captcha state; - void ClearCaptchaState(); - // Returns corresponding native window. gfx::NativeWindow GetNativeWindow() const; @@ -128,12 +137,16 @@ class ExistingUserController : public WmMessageListener::Observer, // The set of UserControllers. std::vector<UserController*> controllers_; - // Used for logging in. - scoped_refptr<Authenticator> authenticator_; + // Used to execute login operations. + scoped_ptr<LoginPerformer> login_performer_; // Index of selected view (user). size_t selected_view_index_; + // Number of login attempts. Used to show help link when > 1 unsuccessful + // logins for the same user. + size_t num_login_attempts_; + // See comment in ProcessWmMessage. base::OneShotTimer<ExistingUserController> delete_timer_; @@ -145,17 +158,11 @@ class ExistingUserController : public WmMessageListener::Observer, // it will be deleted on bubble closing. MessageBubble* bubble_; - // Token representing the specific CAPTCHA challenge. - std::string login_token_; - - // String entered by the user as an answer to a CAPTCHA challenge. - std::string login_captcha_; - // URL that will be opened on browser startup. GURL start_url_; - // Cached credentials data when password change is detected. - GaiaAuthConsumer::ClientLoginResult cached_credentials_; + // Help application used for help dialogs. + scoped_ptr<HelpAppLauncher> help_app_; DISALLOW_COPY_AND_ASSIGN(ExistingUserController); }; diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index 94361be..f6e53b4 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -22,10 +22,12 @@ #include "chrome/browser/chromeos/login/auth_response_handler.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_service.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -45,7 +47,7 @@ namespace chromeos { const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount"; // static -const int GoogleAuthenticator::kClientLoginTimeoutMs = 5000; +const int GoogleAuthenticator::kClientLoginTimeoutMs = 10000; // static const int GoogleAuthenticator::kLocalaccountRetryIntervalMs = 20; @@ -57,21 +59,32 @@ GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer) try_again_(true), checked_for_localaccount_(false) { CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); + // If not already owned, this is a no-op. If it is, this loads the owner's + // public key off of disk. + OwnershipService::GetSharedInstance()->StartLoadOwnerKeyAttempt(); } GoogleAuthenticator::~GoogleAuthenticator() {} void GoogleAuthenticator::CancelClientLogin() { if (gaia_authenticator_->HasPendingFetch()) { + LOG(INFO) << "Canceling ClientLogin attempt."; gaia_authenticator_->CancelRequest(); - OnLoginFailure("Login has timed out; please try again!"); + + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &GoogleAuthenticator::LoadLocalaccount, + std::string(kLocalaccountFile))); + + CheckOffline(LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); } } void GoogleAuthenticator::TryClientLogin() { gaia_authenticator_->StartClientLogin(username_, password_, - GaiaAuthenticator2::kContactsService, + GaiaConstants::kContactsService, login_token_, login_captcha_); ChromeThread::PostDelayedTask( @@ -114,7 +127,7 @@ bool GoogleAuthenticator::AuthenticateToLogin( gaia_authenticator_.reset( new GaiaAuthenticator2(this, - GaiaAuthenticator2::kChromeOSSource, + GaiaConstants::kChromeOSSource, profile->GetRequestContext())); // Will be used for retries. PrepareClientLoginAttempt(password, login_token, login_captcha); @@ -139,7 +152,7 @@ bool GoogleAuthenticator::AuthenticateToUnlock(const std::string& username, ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, - std::string("unlock failed"))); + LoginFailure(LoginFailure::UNLOCK_FAILED))); } return true; } @@ -155,8 +168,9 @@ void GoogleAuthenticator::LoginOffTheRecord() { Details<AuthenticationNotificationDetails>(&details)); consumer_->OnOffTheRecordLoginSuccess(); } else { - LOG(ERROR) << "Could not mount tmpfs cryptohome: " << mount_error; - consumer_->OnLoginFailure("Could not mount tmpfs cryptohome"); + LOG(ERROR) << "Could not mount tmpfs: " << mount_error; + consumer_->OnLoginFailure( + LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); } } @@ -174,9 +188,9 @@ void GoogleAuthenticator::OnClientLoginSuccess( } void GoogleAuthenticator::OnClientLoginFailure( - const GaiaAuthConsumer::GaiaAuthError& error) { + const GoogleServiceAuthError& error) { - if (error.code == GaiaAuthConsumer::REQUEST_CANCELED) { + if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) { if (try_again_) { try_again_ = false; LOG(ERROR) << "Login attempt canceled!?!? Trying again."; @@ -188,7 +202,7 @@ void GoogleAuthenticator::OnClientLoginFailure( ClearClientLoginAttempt(); - if (error.code == GaiaAuthConsumer::TWO_FACTOR) { + if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { LOG(WARNING) << "Two factor authenticated. Sync will not work."; OnClientLoginSuccess(GaiaAuthConsumer::ClientLoginResult()); return; @@ -200,12 +214,14 @@ void GoogleAuthenticator::OnClientLoginFailure( &GoogleAuthenticator::LoadLocalaccount, std::string(kLocalaccountFile))); - if (error.code == GaiaAuthConsumer::NETWORK_ERROR) { + LoginFailure failure_details = LoginFailure::FromNetworkAuthFailure(error); + + if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) { // The fetch failed for network reasons, try offline login. ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, - net::ErrorToString(error.network_error))); + failure_details)); return; } @@ -214,7 +230,7 @@ void GoogleAuthenticator::OnClientLoginFailure( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &GoogleAuthenticator::CheckLocalaccount, - error.data)); + failure_details)); } void GoogleAuthenticator::OnLoginSuccess( @@ -237,11 +253,11 @@ void GoogleAuthenticator::OnLoginSuccess( mount_error == chromeos::kCryptohomeMountErrorKeyFailure) { consumer_->OnPasswordChangeDetected(credentials); } else { - OnLoginFailure("Could not mount cryptohome"); + OnLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)); } } -void GoogleAuthenticator::CheckOffline(const std::string& error) { +void GoogleAuthenticator::CheckOffline(const LoginFailure& error) { LOG(INFO) << "Attempting offline login"; if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey( username_.c_str(), @@ -255,7 +271,7 @@ void GoogleAuthenticator::CheckOffline(const std::string& error) { } } -void GoogleAuthenticator::CheckLocalaccount(const std::string& error) { +void GoogleAuthenticator::CheckLocalaccount(const LoginFailure& error) { { AutoLock for_this_block(localaccount_lock_); LOG(INFO) << "Checking localaccount"; @@ -271,25 +287,30 @@ void GoogleAuthenticator::CheckLocalaccount(const std::string& error) { } } int mount_error = chromeos::kCryptohomeMountErrorNone; - if (!localaccount_.empty() && localaccount_ == username_ && - CrosLibrary::Get()->GetCryptohomeLibrary()->MountForBwsi(&mount_error)) { - LOG(WARNING) << "Logging in with localaccount: " << localaccount_; - consumer_->OnLoginSuccess(username_, GaiaAuthConsumer::ClientLoginResult()); + if (!localaccount_.empty() && localaccount_ == username_) { + if (CrosLibrary::Get()->GetCryptohomeLibrary()->MountForBwsi( + &mount_error)) { + LOG(WARNING) << "Logging in with localaccount: " << localaccount_; + consumer_->OnLoginSuccess(username_, + GaiaAuthConsumer::ClientLoginResult()); + } else { + LOG(ERROR) << "Could not mount tmpfs for local account: " << mount_error; + OnLoginFailure( + LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); + } } else { OnLoginFailure(error); } } -void GoogleAuthenticator::OnLoginFailure(const std::string& error) { +void GoogleAuthenticator::OnLoginFailure(const LoginFailure& error) { // Send notification of failure AuthenticationNotificationDetails details(false); NotificationService::current()->Notify( NotificationType::LOGIN_AUTHENTICATION, NotificationService::AllSources(), Details<AuthenticationNotificationDetails>(&details)); - LOG(WARNING) << "Login failed: " << error; - // TODO(cmasone): what can we do to expose these OS/server-side error strings - // in an internationalizable way? + LOG(WARNING) << "Login failed: " << error.GetErrorString(); consumer_->OnLoginFailure(error); } @@ -313,7 +334,7 @@ void GoogleAuthenticator::ResyncEncryptedData( if (CrosLibrary::Get()->GetCryptohomeLibrary()->Remove(username_)) { OnLoginSuccess(credentials); } else { - OnLoginFailure("Could not destroy your old data!"); + OnLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)); } } @@ -413,19 +434,4 @@ bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary, return true; } -// static -std::string GoogleAuthenticator::Canonicalize( - const std::string& email_address) { - std::vector<std::string> parts; - char at = '@'; - SplitString(email_address, at, &parts); - DCHECK_EQ(parts.size(), 2U) << "email_address should have only one @"; - RemoveChars(parts[0], ".", &parts[0]); - if (parts[0].find('+') != std::string::npos) - parts[0].erase(parts[0].find('+')); - std::string new_email = StringToLowerASCII(JoinString(parts, at)); - LOG(INFO) << "Canonicalized " << email_address << " to " << new_email; - return new_email; -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h index 2fae711..7c71952 100644 --- a/chrome/browser/chromeos/login/google_authenticator.h +++ b/chrome/browser/chromeos/login/google_authenticator.h @@ -4,15 +4,14 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_GOOGLE_AUTHENTICATOR_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_GOOGLE_AUTHENTICATOR_H_ +#pragma once #include <string> #include <vector> #include "base/basictypes.h" -#include "base/file_path.h" #include "base/gtest_prod_util.h" -#include "base/ref_counted.h" -#include "base/sha2.h" +#include "base/scoped_ptr.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/cryptohome_library.h" #include "chrome/browser/chromeos/login/authenticator.h" @@ -23,6 +22,8 @@ class Lock; class Profile; class GaiaAuthenticator2; +class GoogleServiceAuthError; +class LoginFailure; namespace chromeos { @@ -30,7 +31,6 @@ class GoogleAuthenticatorTest; class LoginStatusConsumer; class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { - public: explicit GoogleAuthenticator(LoginStatusConsumer* consumer); virtual ~GoogleAuthenticator(); @@ -74,9 +74,9 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { // These methods must be called on the UI thread, as they make DBus calls // and also call back to the login UI. void OnLoginSuccess(const GaiaAuthConsumer::ClientLoginResult& credentials); - void CheckOffline(const std::string& error); - void CheckLocalaccount(const std::string& error); - void OnLoginFailure(const std::string& error); + void CheckOffline(const LoginFailure& error); + void CheckLocalaccount(const LoginFailure& error); + void OnLoginFailure(const LoginFailure& error); // Call these methods on the UI thread. void RecoverEncryptedData( @@ -85,15 +85,9 @@ class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { void ResyncEncryptedData( const GaiaAuthConsumer::ClientLoginResult& credentials); - // Perform basic canonicalization of |email_address|, taking into account - // that gmail does not consider '.' or caps inside a username to matter. - // For example, c.masone@gmail.com == cMaSone@gmail.com, per - // http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313# - static std::string Canonicalize(const std::string& email_address); - // Callbacks from GaiaAuthenticator2 virtual void OnClientLoginFailure( - const GaiaAuthConsumer::GaiaAuthError& error); + const GoogleServiceAuthError& error); virtual void OnClientLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& credentials); diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index 3789feb..6762f88 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/chromeos/login/google_authenticator.h" + #include <string> #include <vector> @@ -9,16 +11,16 @@ #include "base/file_util.h" #include "base/message_loop.h" #include "base/path_service.h" -#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" #include "chrome/browser/chromeos/cros/mock_library_loader.h" #include "chrome/browser/chromeos/login/client_login_response_handler.h" -#include "chrome/browser/chromeos/login/google_authenticator.h" #include "chrome/browser/chromeos/login/issue_response_handler.h" #include "chrome/browser/chromeos/login/mock_auth_response_handler.h" +#include "chrome/browser/chromeos/login/mock_url_fetchers.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" #include "chrome/common/net/url_fetcher.h" @@ -43,7 +45,7 @@ class MockConsumer : public LoginStatusConsumer { public: MockConsumer() {} ~MockConsumer() {} - MOCK_METHOD1(OnLoginFailure, void(const std::string& error)); + MOCK_METHOD1(OnLoginFailure, void(const LoginFailure& error)); MOCK_METHOD2(OnLoginSuccess, void(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& result)); MOCK_METHOD0(OnOffTheRecordLoginSuccess, void(void)); @@ -133,6 +135,14 @@ class GoogleAuthenticatorTest : public ::testing::Test { auth->SetLocalaccount(""); } + void CancelLogin(GoogleAuthenticator* auth) { + ChromeThread::PostTask( + ChromeThread::UI, + FROM_HERE, + NewRunnableMethod(auth, + &GoogleAuthenticator::CancelClientLogin)); + } + unsigned char fake_hash_[32]; std::string hash_ascii_; std::string username_; @@ -162,51 +172,6 @@ TEST_F(GoogleAuthenticatorTest, SaltToAscii) { EXPECT_EQ("0a010000000000a0", auth->SaltAsAscii()); } -TEST_F(GoogleAuthenticatorTest, EmailAddressNoOp) { - const char lower_case[] = "user@what.com"; - EXPECT_EQ(lower_case, GoogleAuthenticator::Canonicalize(lower_case)); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreCaps) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("user@what.com"), - GoogleAuthenticator::Canonicalize("UsEr@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreDomainCaps) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("user@what.com"), - GoogleAuthenticator::Canonicalize("UsEr@what.COM")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreOneUsernameDot) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("us.er@what.com"), - GoogleAuthenticator::Canonicalize("UsEr@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreManyUsernameDots) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("u.ser@what.com"), - GoogleAuthenticator::Canonicalize("Us.E.r@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreConsecutiveUsernameDots) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("use.r@what.com"), - GoogleAuthenticator::Canonicalize("Us....E.r@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressDifferentOnesRejected) { - EXPECT_NE(GoogleAuthenticator::Canonicalize("who@what.com"), - GoogleAuthenticator::Canonicalize("Us....E.r@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnorePlusSuffix) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("user+cc@what.com"), - GoogleAuthenticator::Canonicalize("user@what.com")); -} - -TEST_F(GoogleAuthenticatorTest, EmailAddressIgnoreMultiPlusSuffix) { - EXPECT_EQ(GoogleAuthenticator::Canonicalize("user+cc+bcc@what.com"), - GoogleAuthenticator::Canonicalize("user@what.com")); -} - TEST_F(GoogleAuthenticatorTest, ReadLocalaccount) { FilePath tmp_file_path = FakeLocalaccountFile(bytes_as_ascii_); @@ -218,7 +183,8 @@ TEST_F(GoogleAuthenticatorTest, ReadLocalaccount) { TEST_F(GoogleAuthenticatorTest, ReadLocalaccountTrailingWS) { FilePath tmp_file_path = - FakeLocalaccountFile(StringPrintf("%s\n", bytes_as_ascii_.c_str())); + FakeLocalaccountFile(base::StringPrintf("%s\n", + bytes_as_ascii_.c_str())); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); ReadLocalaccountFile(auth.get(), tmp_file_path.BaseName().value()); @@ -352,15 +318,14 @@ TEST_F(GoogleAuthenticatorTest, LoginNetFailure) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - int error_no = net::ERR_CONNECTION_RESET; - std::string data(net::ErrorToString(error_no)); + GoogleServiceAuthError error = + GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET); - GaiaAuthConsumer::GaiaAuthError error; - error.code = GaiaAuthConsumer::NETWORK_ERROR; - error.network_error = error_no; + LoginFailure failure = + LoginFailure::FromNetworkAuthFailure(error); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(data)) + EXPECT_CALL(consumer, OnLoginFailure(failure)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_)) @@ -377,8 +342,8 @@ TEST_F(GoogleAuthenticatorTest, LoginDenied) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GaiaAuthConsumer::GaiaAuthError error; - error.code = GaiaAuthConsumer::PERMISSION_DENIED; + GoogleServiceAuthError client_error( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); MockConsumer consumer; EXPECT_CALL(consumer, OnLoginFailure(_)) @@ -387,7 +352,61 @@ TEST_F(GoogleAuthenticatorTest, LoginDenied) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnClientLoginFailure(error); + auth->OnClientLoginFailure(client_error); + message_loop.RunAllPending(); +} + +TEST_F(GoogleAuthenticatorTest, LoginAccountDisabled) { + MessageLoopForUI message_loop; + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + + GoogleServiceAuthError client_error( + GoogleServiceAuthError::ACCOUNT_DISABLED); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(_)) + .Times(1) + .RetiresOnSaturation(); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + PrepForLogin(auth.get()); + auth->OnClientLoginFailure(client_error); + message_loop.RunAllPending(); +} + +TEST_F(GoogleAuthenticatorTest, LoginAccountDeleted) { + MessageLoopForUI message_loop; + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + + GoogleServiceAuthError client_error( + GoogleServiceAuthError::ACCOUNT_DELETED); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(_)) + .Times(1) + .RetiresOnSaturation(); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + PrepForLogin(auth.get()); + auth->OnClientLoginFailure(client_error); + message_loop.RunAllPending(); +} + +TEST_F(GoogleAuthenticatorTest, LoginServiceUnavailable) { + MessageLoopForUI message_loop; + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + + GoogleServiceAuthError client_error( + GoogleServiceAuthError::SERVICE_UNAVAILABLE); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(_)) + .Times(1) + .RetiresOnSaturation(); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + PrepForLogin(auth.get()); + auth->OnClientLoginFailure(client_error); message_loop.RunAllPending(); } @@ -395,22 +414,22 @@ TEST_F(GoogleAuthenticatorTest, CaptchaErrorOutputted) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - // TODO(chron): Swap out this captcha passing for actual captcha parsing. - GaiaAuthConsumer::GaiaAuthError error; - error.code = GaiaAuthConsumer::PERMISSION_DENIED; - error.data = "Url=http://www.google.com/login/captcha\n" - "Error=CaptchaRequired\n" - "CaptchaToken=DQAAAGgA...dkI1LK9\n" - "CaptchaUrl=Captcha?ctoken=...\n"; + GoogleServiceAuthError auth_error = + GoogleServiceAuthError::FromCaptchaChallenge( + "CCTOKEN", + GURL("http://www.google.com/accounts/Captcha?ctoken=CCTOKEN"), + GURL("http://www.google.com/login/captcha")); + + LoginFailure failure = LoginFailure::FromNetworkAuthFailure(auth_error); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(error.data)) + EXPECT_CALL(consumer, OnLoginFailure(failure)) .Times(1) .RetiresOnSaturation(); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnClientLoginFailure(error); + auth->OnClientLoginFailure(auth_error); message_loop.RunAllPending(); } @@ -418,9 +437,8 @@ TEST_F(GoogleAuthenticatorTest, OfflineLogin) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GaiaAuthConsumer::GaiaAuthError error; - error.code = GaiaAuthConsumer::NETWORK_ERROR; - error.network_error = net::ERR_CONNECTION_RESET; + GoogleServiceAuthError auth_error( + GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET)); MockConsumer consumer; EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)) @@ -435,7 +453,7 @@ TEST_F(GoogleAuthenticatorTest, OfflineLogin) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnClientLoginFailure(error); + auth->OnClientLoginFailure(auth_error); message_loop.RunAllPending(); } @@ -473,20 +491,37 @@ TEST_F(GoogleAuthenticatorTest, CheckLocalaccount) { PrepForLogin(auth.get()); auth->SetLocalaccount(username_); - auth->CheckLocalaccount(std::string()); + auth->CheckLocalaccount(LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); } +namespace { + // Compatible with LoginStatusConsumer::OnLoginSuccess() -static void Quit(const std::string& username, - const GaiaAuthConsumer::ClientLoginResult& credentials) { +static void OnSuccessQuit( + const std::string& username, + const GaiaAuthConsumer::ClientLoginResult& credentials) { + MessageLoop::current()->Quit(); +} + +static void OnSuccessQuitAndFail( + const std::string& username, + const GaiaAuthConsumer::ClientLoginResult& credentials) { + ADD_FAILURE() << "Login should NOT have succeeded!"; MessageLoop::current()->Quit(); } + // Compatible with LoginStatusConsumer::OnLoginFailure() -static void QuitAndFail(const std::string& error) { +static void OnFailQuit(const LoginFailure& error) { + MessageLoop::current()->Quit(); +} + +static void OnFailQuitAndFail(const LoginFailure& error) { ADD_FAILURE() << "Login should have succeeded!"; MessageLoop::current()->Quit(); } +} // anonymous namespace + TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { // This test checks the logic that governs asynchronously reading the // localaccount name off disk and trying to authenticate against it @@ -495,17 +530,15 @@ TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { ChromeThread ui_thread(ChromeThread::UI, &message_loop); MockConsumer consumer; - ON_CALL(consumer, OnLoginSuccess(username_, _)) - .WillByDefault(Invoke(Quit)); EXPECT_CALL(consumer, OnLoginSuccess(username_, _)) - .Times(1) + .WillOnce(Invoke(OnSuccessQuit)) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, MountForBwsi(_)) .WillOnce(Return(true)) .RetiresOnSaturation(); // Enable the test to terminate (and fail), even if the login fails. ON_CALL(consumer, OnLoginFailure(_)) - .WillByDefault(Invoke(QuitAndFail)); + .WillByDefault(Invoke(OnFailQuitAndFail)); // Manually prep for login, so that localaccount isn't set for us. scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); @@ -518,7 +551,7 @@ TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { ChromeThread::UI, FROM_HERE, NewRunnableMethod(auth.get(), &GoogleAuthenticator::CheckLocalaccount, - std::string("fail"))); + LoginFailure(LoginFailure::LOGIN_TIMED_OUT))); message_loop.RunAllPending(); // The foregoing has now rescheduled itself in a few ms because we don't // yet have the localaccount loaded off disk. @@ -537,8 +570,6 @@ TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { TEST_F(GoogleAuthenticatorTest, FullLogin) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GURL source(AuthResponseHandler::kTokenAuthUrl); - URLRequestStatus status(URLRequestStatus::SUCCESS, 0); chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); MockConsumer consumer; @@ -549,15 +580,13 @@ TEST_F(GoogleAuthenticatorTest, FullLogin) { .WillOnce(Return(true)) .RetiresOnSaturation(); - ON_CALL(*mock_library_, GetSystemSalt()) - .WillByDefault(Return(salt_v)); EXPECT_CALL(*mock_library_, GetSystemSalt()) - .Times(1) + .WillOnce(Return(salt_v)) .RetiresOnSaturation(); TestingProfile profile; - MockFactory factory; + MockFactory<MockFetcher> factory; URLFetcher::set_factory(&factory); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); @@ -568,4 +597,109 @@ TEST_F(GoogleAuthenticatorTest, FullLogin) { message_loop.RunAllPending(); } +TEST_F(GoogleAuthenticatorTest, CancelLogin) { + MessageLoop message_loop(MessageLoop::TYPE_UI); + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); + + MockConsumer consumer; + // The expected case. + EXPECT_CALL(consumer, OnLoginFailure(_)) + .WillOnce(Invoke(OnFailQuit)) + .RetiresOnSaturation(); + + // A failure case, but we still want the test to finish gracefully. + ON_CALL(consumer, OnLoginSuccess(username_, _)) + .WillByDefault(Invoke(OnSuccessQuitAndFail)); + + // Stuff we expect to happen along the way. + EXPECT_CALL(*mock_library_, GetSystemSalt()) + .WillOnce(Return(salt_v)) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_library_, CheckKey(username_, _)) + .WillOnce(Return(false)) + .RetiresOnSaturation(); + + TestingProfile profile; + + // This is how we inject fake URLFetcher objects, with a factory. + // This factory creates fake URLFetchers that Start() a fake fetch attempt + // and then come back on the UI thread after a small delay. They expect to + // be canceled before they come back, and the test will fail if they are not. + MockFactory<ExpectCanceledFetcher> factory; + URLFetcher::set_factory(&factory); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + + // For when |auth| tries to load the localaccount file. + ChromeThread file_thread(ChromeThread::FILE); + file_thread.Start(); + + // Start an authentication attempt, which will kick off a URL "fetch" that + // we expect to cancel before it completes. + auth->AuthenticateToLogin( + &profile, username_, hash_ascii_, std::string(), std::string()); + + // Post a task to cancel the login attempt. + CancelLogin(auth.get()); + + URLFetcher::set_factory(NULL); + + // Run the UI thread until we exit it gracefully. + message_loop.Run(); +} + +TEST_F(GoogleAuthenticatorTest, CancelLoginAlreadyGotLocalaccount) { + MessageLoop message_loop(MessageLoop::TYPE_UI); + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); + + MockConsumer consumer; + // The expected case. + EXPECT_CALL(consumer, OnLoginFailure(_)) + .WillOnce(Invoke(OnFailQuit)) + .RetiresOnSaturation(); + + // A failure case, but we still want the test to finish gracefully. + ON_CALL(consumer, OnLoginSuccess(username_, _)) + .WillByDefault(Invoke(OnSuccessQuitAndFail)); + + // Stuff we expect to happen along the way. + EXPECT_CALL(*mock_library_, GetSystemSalt()) + .WillOnce(Return(salt_v)) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_library_, CheckKey(username_, _)) + .WillOnce(Return(false)) + .RetiresOnSaturation(); + + TestingProfile profile; + + // This is how we inject fake URLFetcher objects, with a factory. + // This factory creates fake URLFetchers that Start() a fake fetch attempt + // and then come back on the UI thread after a small delay. They expect to + // be canceled before they come back, and the test will fail if they are not. + MockFactory<ExpectCanceledFetcher> factory; + URLFetcher::set_factory(&factory); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + + // This time, instead of allowing |auth| to go get the localaccount file + // itself, we simulate the case where the file is already loaded, which + // happens when this isn't the first login since chrome started. + ReadLocalaccountFile(auth.get(), ""); + + // Start an authentication attempt, which will kick off a URL "fetch" that + // we expect to cancel before it completes. + auth->AuthenticateToLogin( + &profile, username_, hash_ascii_, std::string(), std::string()); + + // Post a task to cancel the login attempt. + CancelLogin(auth.get()); + + URLFetcher::set_factory(NULL); + + // Run the UI thread until we exit it gracefully. + message_loop.Run(); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/helper.cc b/chrome/browser/chromeos/login/helper.cc index 4ea9ed4..d2da133 100644 --- a/chrome/browser/chromeos/login/helper.cc +++ b/chrome/browser/chromeos/login/helper.cc @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/chromeos/login/helper.h" + #include "app/resource_bundle.h" +#include "chrome/browser/google/google_util.h" #include "gfx/canvas_skia.h" +#include "googleurl/src/gurl.h" #include "grit/theme_resources.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "views/controls/throbber.h" @@ -23,6 +27,9 @@ const int kThrobberStartDelayMs = 500; const SkColor kBackgroundCenterColor = SkColorSetRGB(41, 50, 67); const SkColor kBackgroundEdgeColor = SK_ColorBLACK; +const char kAccountRecoveryHelpUrl[] = + "http://www.google.com/support/accounts/bin/answer.py?answer=48598"; + class BackgroundPainter : public views::Painter { public: BackgroundPainter() {} @@ -87,5 +94,9 @@ gfx::Rect CalculateScreenBounds(const gfx::Size& size) { return bounds; } +GURL GetAccountRecoveryHelpUrl() { + return google_util::AppendGoogleLocaleParam(GURL(kAccountRecoveryHelpUrl)); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/helper.h b/chrome/browser/chromeos/login/helper.h index a98d2f2..4e7de2a 100644 --- a/chrome/browser/chromeos/login/helper.h +++ b/chrome/browser/chromeos/login/helper.h @@ -6,9 +6,17 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_HELPER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_HELPER_H_ +#pragma once #include "third_party/skia/include/core/SkColor.h" +class GURL; + +namespace gfx { +class Rect; +class Size; +} // namespace gfx + namespace views { class Painter; class Throbber; @@ -30,6 +38,9 @@ views::Painter* CreateBackgroundPainter(); // |size| is not empty. Otherwise the whole monitor is occupied. gfx::Rect CalculateScreenBounds(const gfx::Size& size); +// Returns URL used for account recovery. +GURL GetAccountRecoveryHelpUrl(); + // Define the constants in |login| namespace to avoid potential // conflict with other chromeos components. namespace login { @@ -41,7 +52,7 @@ enum Command { }; // Gap between edge and image view, and image view and controls. -const int kBorderSize = 4; +const int kBorderSize = 6; // The size of user image. const int kUserImageSize = 256; @@ -52,6 +63,15 @@ const SkColor kBackgroundColor = SK_ColorWHITE; // Text color on the login controls. const SkColor kTextColor = SK_ColorWHITE; +// Default size of the OOBE screen. Includes 10px shadow from each side. +// See rounded_rect_painter.cc for border definitions. +const int kWizardScreenWidth = 700; +const int kWizardScreenHeight = 416; + +const int kScreenCornerRadius = 10; +const int kUserCornerRadius = 5; + + } // namespace login } // namespace chromeos diff --git a/chrome/browser/chromeos/login/image_decoder.h b/chrome/browser/chromeos/login/image_decoder.h index fdb9e1c..02ad1ce 100644 --- a/chrome/browser/chromeos/login/image_decoder.h +++ b/chrome/browser/chromeos/login/image_decoder.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_IMAGE_DECODER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_IMAGE_DECODER_H_ +#pragma once #include <vector> diff --git a/chrome/browser/chromeos/login/image_downloader.cc b/chrome/browser/chromeos/login/image_downloader.cc index dfcd797..367e27e 100644 --- a/chrome/browser/chromeos/login/image_downloader.cc +++ b/chrome/browser/chromeos/login/image_downloader.cc @@ -6,6 +6,8 @@ #include "base/logging.h" #include "base/message_loop.h" +#include "base/string_util.h" +#include "base/stringprintf.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/profile_manager.h" @@ -30,7 +32,7 @@ ImageDownloader::ImageDownloader(ImageDecoder::Delegate* delegate, ProfileManager::GetDefaultProfile()->GetRequestContext()); if (!auth_token.empty()) { image_fetcher_->set_extra_request_headers( - StringPrintf(kAuthorizationHeader, auth_token.c_str())); + base::StringPrintf(kAuthorizationHeader, auth_token.c_str())); } image_fetcher_->Start(); } diff --git a/chrome/browser/chromeos/login/image_downloader.h b/chrome/browser/chromeos/login/image_downloader.h index 61d773c..06193bf 100644 --- a/chrome/browser/chromeos/login/image_downloader.h +++ b/chrome/browser/chromeos/login/image_downloader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_IMAGE_DOWNLOADER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_IMAGE_DOWNLOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/issue_response_handler.cc b/chrome/browser/chromeos/login/issue_response_handler.cc index bfc1ae2..54abf99 100644 --- a/chrome/browser/chromeos/login/issue_response_handler.cc +++ b/chrome/browser/chromeos/login/issue_response_handler.cc @@ -23,9 +23,8 @@ URLFetcher* IssueResponseHandler::Handle( const std::string& to_process, URLFetcher::Delegate* catcher) { LOG(INFO) << "Handling IssueAuthToken response"; - token_url_.assign(StringPrintf("%s%s", - AuthResponseHandler::kTokenAuthUrl, - to_process.c_str())); + token_url_.assign(base::StringPrintf("%s%s", + AuthResponseHandler::kTokenAuthUrl, to_process.c_str())); URLFetcher* fetcher = new URLFetcher(GURL(token_url_), URLFetcher::GET, catcher); fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); diff --git a/chrome/browser/chromeos/login/issue_response_handler.h b/chrome/browser/chromeos/login/issue_response_handler.h index 4741c80..e724111 100644 --- a/chrome/browser/chromeos/login/issue_response_handler.h +++ b/chrome/browser/chromeos/login/issue_response_handler.h @@ -4,10 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_ISSUE_RESPONSE_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_ISSUE_RESPONSE_HANDLER_H_ +#pragma once #include <string> -#include "base/logging.h" +#include "base/basictypes.h" #include "chrome/browser/chromeos/login/auth_response_handler.h" class URLRequestContextGetter; diff --git a/chrome/browser/chromeos/login/language_switch_menu.cc b/chrome/browser/chromeos/login/language_switch_menu.cc index d93e8fe..5f5d8c6 100644 --- a/chrome/browser/chromeos/login/language_switch_menu.cc +++ b/chrome/browser/chromeos/login/language_switch_menu.cc @@ -4,14 +4,15 @@ #include "chrome/browser/chromeos/login/language_switch_menu.h" -#include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/keyboard_library.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/login/screen_observer.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "views/widget/widget_gtk.h" @@ -69,18 +70,9 @@ std::wstring LanguageSwitchMenu::GetCurrentLocaleName() const { language_list_->GetIndexFromLocale(locale)); }; -// Currently, views::Menu is implemented directly with the Gtk -// widgets. So we use native gtk callbacks to get its future size. -int LanguageSwitchMenu::GetFirstLevelMenuWidth() const { - DCHECK(menu_ != NULL); - GtkRequisition box_size; - gtk_widget_size_request(menu_->GetNativeMenu(), &box_size); - return box_size.width; -} - void LanguageSwitchMenu::SetFirstLevelMenuWidth(int width) { DCHECK(menu_ != NULL); - gtk_widget_set_size_request(menu_->GetNativeMenu(), width, -1); + menu_->SetMinimumWidth(width); } // static @@ -97,12 +89,13 @@ void LanguageSwitchMenu::SwitchLanguage(const std::string& locale) { prefs->SavePersistentPrefs(); // Switch the locale. - ResourceBundle::ReloadSharedInstance(UTF8ToWide(locale)); + ResourceBundle::ReloadSharedInstance(locale); // Enable the keyboard layouts that are necessary for the new locale. - chromeos::input_method::EnableInputMethods( - locale, chromeos::input_method::kKeyboardLayoutsOnly, - kHardwareKeyboardLayout); + input_method::EnableInputMethods( + locale, input_method::kKeyboardLayoutsOnly, + CrosLibrary::Get()->GetKeyboardLibrary()-> + GetHardwareKeyboardLayoutName()); // The following line does not seem to affect locale anyhow. Maybe in // future.. diff --git a/chrome/browser/chromeos/login/language_switch_menu.h b/chrome/browser/chromeos/login/language_switch_menu.h index 849d537..a3db06e 100644 --- a/chrome/browser/chromeos/login/language_switch_menu.h +++ b/chrome/browser/chromeos/login/language_switch_menu.h @@ -4,16 +4,17 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LANGUAGE_SWITCH_MENU_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_LANGUAGE_SWITCH_MENU_H_ +#pragma once #include <string> #include "app/menus/simple_menu_model.h" #include "base/scoped_ptr.h" #include "chrome/browser/language_combobox_model.h" +#include "testing/gtest/include/gtest/gtest_prod.h" #include "views/controls/menu/menu_2.h" #include "views/controls/menu/view_menu_delegate.h" #include "views/view.h" -#include "testing/gtest/include/gtest/gtest_prod.h" class WizardControllerTest_SwitchLanguage_Test; @@ -32,8 +33,7 @@ class LanguageSwitchMenu : public views::ViewMenuDelegate, // Returns current locale name to be placed on the language menu-button. std::wstring GetCurrentLocaleName() const; - // Returns original width of the first level menu to be shown when called. - int GetFirstLevelMenuWidth() const; + // Sets the minimum width of the first level menu to be shown. void SetFirstLevelMenuWidth(int width); void set_menu_offset(int delta_x, int delta_y) { diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc index dd89fb3..85a99ba 100644 --- a/chrome/browser/chromeos/login/login_browsertest.cc +++ b/chrome/browser/chromeos/login/login_browsertest.cc @@ -6,7 +6,6 @@ #include "base/time.h" #include "chrome/browser/browser.h" #include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" -#include "chrome/browser/profile_manager.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" #include "chrome/browser/chromeos/cros/mock_input_method_library.h" #include "chrome/browser/chromeos/cros/mock_keyboard_library.h" @@ -14,13 +13,14 @@ #include "chrome/browser/chromeos/cros/mock_network_library.h" #include "chrome/browser/chromeos/cros/mock_power_library.h" #include "chrome/browser/chromeos/cros/mock_screen_lock_library.h" -#include "chrome/browser/chromeos/cros/mock_synaptics_library.h" #include "chrome/browser/chromeos/cros/mock_system_library.h" +#include "chrome/browser/chromeos/cros/mock_touchpad_library.h" +#include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chromeos { using ::testing::_; @@ -50,8 +50,10 @@ class LoginTestBase : public InProcessBrowserTest { testApi_->SetPowerLibrary(&mock_power_library_, false); EXPECT_CALL(mock_power_library_, battery_time_to_empty()) .WillRepeatedly((Return(base::TimeDelta::FromMinutes(42)))); + EXPECT_CALL(mock_power_library_, battery_time_to_full()) + .WillRepeatedly((Return(base::TimeDelta::FromMinutes(24)))); - testApi_->SetSynapticsLibrary(&mock_synaptics_library_, false); + testApi_->SetTouchpadLibrary(&mock_touchpad_library_, false); testApi_->SetCryptohomeLibrary(&mock_cryptohome_library_, false); testApi_->SetScreenLockLibrary(&mock_screen_lock_library_, false); testApi_->SetSystemLibrary(&mock_system_library_, false); @@ -65,7 +67,7 @@ class LoginTestBase : public InProcessBrowserTest { NiceMock<MockNetworkLibrary> mock_network_library_; NiceMock<MockPowerLibrary> mock_power_library_; NiceMock<MockScreenLockLibrary> mock_screen_lock_library_; - NiceMock<MockSynapticsLibrary> mock_synaptics_library_; + NiceMock<MockTouchpadLibrary> mock_touchpad_library_; NiceMock<MockSystemLibrary> mock_system_library_; ImePropertyList ime_properties_; chromeos::CrosLibrary::TestApi* testApi_; @@ -81,9 +83,8 @@ class LoginUserTest : public LoginTestBase { } virtual void SetUpCommandLine(CommandLine* command_line) { - command_line->AppendSwitchWithValue( - switches::kLoginUser, "TestUser@gmail.com"); - command_line->AppendSwitchWithValue(switches::kLoginProfile, "user"); + command_line->AppendSwitchASCII(switches::kLoginUser, "TestUser@gmail.com"); + command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); command_line->AppendSwitch(switches::kNoFirstRun); } }; @@ -96,7 +97,7 @@ class LoginProfileTest : public LoginTestBase { } virtual void SetUpCommandLine(CommandLine* command_line) { - command_line->AppendSwitchWithValue(switches::kLoginProfile, "user"); + command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); command_line->AppendSwitch(switches::kNoFirstRun); } }; diff --git a/chrome/browser/chromeos/login/login_html_dialog.cc b/chrome/browser/chromeos/login/login_html_dialog.cc index a895626..a05c7de 100644 --- a/chrome/browser/chromeos/login/login_html_dialog.cc +++ b/chrome/browser/chromeos/login/login_html_dialog.cc @@ -4,18 +4,35 @@ #include "chrome/browser/chromeos/login/login_html_dialog.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/profile_manager.h" -#include "chrome/browser/views/browser_dialogs.h" +#include "chrome/browser/views/html_dialog_view.h" #include "gfx/native_widget_types.h" +#include "gfx/rect.h" #include "gfx/size.h" #include "views/window/window.h" namespace chromeos { namespace { +// Default width/height ratio of screen size. +const float kDefaultWidthRatio = 0.8; +const float kDefaultHeightRatio = 0.8; -const int kDefaultWidth = 800; -const int kDefaultHeight = 600; +// Custom HtmlDialogView with disabled context menu. +class HtmlDialogWithoutContextMenuView : public HtmlDialogView { + public: + HtmlDialogWithoutContextMenuView(Profile* profile, + HtmlDialogUIDelegate* delegate) + : HtmlDialogView(profile, delegate) {} + virtual ~HtmlDialogWithoutContextMenuView() {} + + // TabContentsDelegate implementation. + bool HandleContextMenu(const ContextMenuParams& params) { + // Disable context menu. + return true; + } +}; } // namespace @@ -30,6 +47,9 @@ LoginHtmlDialog::LoginHtmlDialog(Delegate* delegate, parent_window_(parent_window), title_(title), url_(url) { + gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size(0, 0))); + width_ = static_cast<int>(kDefaultWidthRatio * screen_bounds.width()); + height_ = static_cast<int>(kDefaultHeightRatio * screen_bounds.height()); } LoginHtmlDialog::~LoginHtmlDialog() { @@ -37,9 +57,18 @@ LoginHtmlDialog::~LoginHtmlDialog() { } void LoginHtmlDialog::Show() { - browser::ShowHtmlDialogView(parent_window_, - ProfileManager::GetDefaultProfile(), - this); + HtmlDialogWithoutContextMenuView* html_view = + new HtmlDialogWithoutContextMenuView(ProfileManager::GetDefaultProfile(), + this); + views::Window::CreateChromeWindow(parent_window_, gfx::Rect(), html_view); + html_view->InitDialog(); + html_view->window()->Show(); +} + +void LoginHtmlDialog::SetDialogSize(int width, int height) { + DCHECK(width >= 0 && height >= 0); + width_ = width; + height_ = height; } /////////////////////////////////////////////////////////////////////////////// @@ -57,7 +86,7 @@ void LoginHtmlDialog::OnCloseContents(TabContents* source, } void LoginHtmlDialog::GetDialogSize(gfx::Size* size) const { - size->SetSize(kDefaultWidth, kDefaultHeight); + size->SetSize(width_, height_); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/login_html_dialog.h b/chrome/browser/chromeos/login/login_html_dialog.h index 81c6628..367bb56 100644 --- a/chrome/browser/chromeos/login/login_html_dialog.h +++ b/chrome/browser/chromeos/login/login_html_dialog.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_HTML_DIALOG_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_HTML_DIALOG_H_ +#pragma once #include <string> @@ -34,6 +35,11 @@ class LoginHtmlDialog : public HtmlDialogUIDelegate { // Shows created dialog. void Show(); + // Overrides default width/height for dialog. + void SetDialogSize(int width, int height); + + void set_url(const GURL& url) { url_ = url; } + protected: // HtmlDialogUIDelegate implementation. virtual bool IsDialogModal() const { return true; } @@ -54,6 +60,10 @@ class LoginHtmlDialog : public HtmlDialogUIDelegate { std::wstring title_; GURL url_; + // Dialog display size. + int width_; + int height_; + DISALLOW_COPY_AND_ASSIGN(LoginHtmlDialog); }; diff --git a/chrome/browser/chromeos/login/login_screen.cc b/chrome/browser/chromeos/login/login_screen.cc index 9e1d886..6f34d0e 100644 --- a/chrome/browser/chromeos/login/login_screen.cc +++ b/chrome/browser/chromeos/login/login_screen.cc @@ -17,12 +17,14 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/notification_service.h" +#include "grit/generated_resources.h" #include "grit/theme_resources.h" namespace chromeos { @@ -41,7 +43,7 @@ LoginScreen::~LoginScreen() { } NewUserView* LoginScreen::AllocateView() { - return new NewUserView(this, true); + return new NewUserView(this, true, true); } void LoginScreen::OnLogin(const std::string& username, @@ -73,7 +75,8 @@ void LoginScreen::ClearErrors() { bubble_->Close(); } -void LoginScreen::OnLoginFailure(const std::string& error) { +void LoginScreen::OnLoginFailure(const LoginFailure& failure) { + const std::string error = failure.GetErrorString(); LOG(INFO) << "LoginManagerView: OnLoginFailure() " << error; NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary(); @@ -84,7 +87,7 @@ void LoginScreen::OnLoginFailure(const std::string& error) { } else if (!network->Connected()) { ShowError(IDS_LOGIN_ERROR_OFFLINE_FAILED_NETWORK_NOT_CONNECTED, error); } else { - ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error); + ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error); } view()->ClearAndEnablePassword(); @@ -100,28 +103,32 @@ void LoginScreen::OnLoginSuccess(const std::string& username, void LoginScreen::OnOffTheRecordLoginSuccess() { delegate()->GetObserver(this)->OnExit(ScreenObserver::LOGIN_GUEST_SELECTED); - AppendStartUrlToCmdline(); - LoginUtils::Get()->CompleteOffTheRecordLogin(); +} + +void LoginScreen::OnHelpLinkActivated() { + AddStartUrl(GetAccountRecoveryHelpUrl()); + OnLoginOffTheRecord(); } void LoginScreen::AppendStartUrlToCmdline() { - if (start_url_.is_valid()) { - CommandLine::ForCurrentProcess()->AppendLooseValue( - UTF8ToWide(start_url_.spec())); - } + if (start_url_.is_valid()) + CommandLine::ForCurrentProcess()->AppendArg(start_url_.spec()); } void LoginScreen::ShowError(int error_id, const std::string& details) { ClearErrors(); std::wstring error_text = l10n_util::GetString(error_id); - if (!details.empty()) - error_text += L"\n" + ASCIIToWide(details); + // TODO(dpolukhin): show detailed error info. |details| string contains + // low level error info that is not localized and even is not user friendly. + // For now just ignore it because error_text contains all required information + // for end users, developers can see details string in Chrome logs. bubble_ = MessageBubble::Show( view()->GetWidget(), view()->GetPasswordBounds(), BubbleBorder::LEFT_TOP, ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), error_text, + l10n_util::GetString(IDS_CANT_ACCESS_ACCOUNT_BUTTON), this); } diff --git a/chrome/browser/chromeos/login/login_screen.h b/chrome/browser/chromeos/login/login_screen.h index 3b3645c..59bcd0e 100644 --- a/chrome/browser/chromeos/login/login_screen.h +++ b/chrome/browser/chromeos/login/login_screen.h @@ -4,15 +4,16 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SCREEN_H_ +#pragma once #include <string> #include "base/ref_counted.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/new_user_view.h" #include "chrome/browser/chromeos/login/view_screen.h" -#include "chrome/browser/views/info_bubble.h" namespace chromeos { @@ -21,7 +22,7 @@ class MessageBubble; class LoginScreen : public ViewScreen<NewUserView>, public NewUserView::Delegate, public LoginStatusConsumer, - public InfoBubbleDelegate { + public MessageBubbleDelegate { public: explicit LoginScreen(WizardScreenDelegate* delegate); virtual ~LoginScreen(); @@ -37,9 +38,10 @@ class LoginScreen : public ViewScreen<NewUserView>, virtual void OnCreateAccount(); virtual void AddStartUrl(const GURL& start_url) { start_url_ = start_url; } virtual void ClearErrors(); + virtual void NavigateAway() {} // Overridden from LoginStatusConsumer. - virtual void OnLoginFailure(const std::string& error); + virtual void OnLoginFailure(const LoginFailure& error); virtual void OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& credentials); virtual void OnOffTheRecordLoginSuccess(); @@ -51,6 +53,7 @@ class LoginScreen : public ViewScreen<NewUserView>, } virtual bool CloseOnEscape() { return true; } virtual bool FadeInOnShow() { return false; } + virtual void OnHelpLinkActivated(); private: // ViewScreen<NewUserView>: diff --git a/chrome/browser/chromeos/login/login_screen_browsertest.cc b/chrome/browser/chromeos/login/login_screen_browsertest.cc index 3184426..35425e3 100644 --- a/chrome/browser/chromeos/login/login_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/login_screen_browsertest.cc @@ -28,21 +28,31 @@ const char kPassword[] = "test_password"; class LoginScreenTest : public WizardInProcessBrowserTest { public: - LoginScreenTest(): WizardInProcessBrowserTest("login") { + LoginScreenTest(): WizardInProcessBrowserTest("login"), + mock_cryptohome_library_(NULL), + mock_login_library_(NULL), + mock_network_library_(NULL) { } protected: + MockCryptohomeLibrary *mock_cryptohome_library_; + MockLoginLibrary *mock_login_library_; + MockNetworkLibrary *mock_network_library_; + virtual void SetUpInProcessBrowserTestFixture() { WizardInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); - InitStatusAreaMocks(); - SetStatusAreaMocksExpectations(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->SetStatusAreaMocksExpectations(); + + mock_network_library_ = cros_mock_->mock_network_library(); mock_login_library_ = new MockLoginLibrary(); EXPECT_CALL(*mock_login_library_, EmitLoginPromptReady()) .Times(1); - test_api()->SetLoginLibrary(mock_login_library_, true); + cros_mock_->test_api()->SetLoginLibrary(mock_login_library_, true); - InitMockCryptohomeLibrary(); + cros_mock_->InitMockCryptohomeLibrary(); + mock_cryptohome_library_ = cros_mock_->mock_cryptohome_library(); EXPECT_CALL(*mock_cryptohome_library_, IsMounted()) .Times(AnyNumber()) .WillRepeatedly((Return(true))); @@ -51,12 +61,10 @@ class LoginScreenTest : public WizardInProcessBrowserTest { virtual void TearDownInProcessBrowserTestFixture() { WizardInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); - test_api()->SetLoginLibrary(NULL, false); + cros_mock_->test_api()->SetLoginLibrary(NULL, false); } private: - MockLoginLibrary* mock_login_library_; - DISALLOW_COPY_AND_ASSIGN(LoginScreenTest); }; @@ -77,10 +85,6 @@ IN_PROC_BROWSER_TEST_F(LoginScreenTest, TestBasic) { controller()->set_observer(mock_screen_observer.get()); NewUserView* login = controller()->GetLoginScreen()->view(); - // NOTE: When adding new controls check RecreateNativeControls() - // that |sign_in_button_| is added with correct index. - // See kSignInButtonFocusOrderIndex constant. - EXPECT_EQ(9, login->GetChildViewCount()); login->SetUsername(kUsername); login->SetPassword(kPassword); diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index 1fa0104..b762e38 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -4,19 +4,95 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_STATUS_CONSUMER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_STATUS_CONSUMER_H_ +#pragma once #include <string> #include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "net/base/net_errors.h" namespace chromeos { +class LoginFailure { + public: + enum FailureReason { + NONE, + COULD_NOT_MOUNT_CRYPTOHOME, + COULD_NOT_MOUNT_TMPFS, + DATA_REMOVAL_FAILED, // Could not destroy your old data + LOGIN_TIMED_OUT, + UNLOCK_FAILED, + NETWORK_AUTH_FAILED, // Could not authenticate against Google + }; + + explicit LoginFailure(FailureReason reason) + : reason_(reason), + error_(GoogleServiceAuthError::NONE) { + DCHECK(reason != NETWORK_AUTH_FAILED); + } + + inline bool operator==(const LoginFailure &b) const { + if (reason_ != b.reason_) { + return false; + } + if (reason_ == NETWORK_AUTH_FAILED) { + return error_ == b.error_; + } + return true; + } + + static LoginFailure FromNetworkAuthFailure( + const GoogleServiceAuthError& error) { + return LoginFailure(NETWORK_AUTH_FAILED, error); + } + + static LoginFailure None() { + return LoginFailure(NONE); + } + + const std::string GetErrorString() const { + switch (reason_) { + case DATA_REMOVAL_FAILED: + return "Could not destroy your old data."; + case COULD_NOT_MOUNT_CRYPTOHOME: + return "Could not mount cryptohome."; + case COULD_NOT_MOUNT_TMPFS: + return "Could not mount tmpfs."; + case LOGIN_TIMED_OUT: + return "Login timed out. Please try again."; + case UNLOCK_FAILED: + return "Unlock failed."; + case NETWORK_AUTH_FAILED: + if (error_.state() == GoogleServiceAuthError::CONNECTION_FAILED) { + return net::ErrorToString(error_.network_error()); + } + return "Google authentication failed."; + default: + NOTREACHED(); + return std::string(); + } + } + + const GoogleServiceAuthError& error() const { return error_; } + const FailureReason& reason() const { return reason_; } + + private: + LoginFailure(FailureReason reason, GoogleServiceAuthError error) + : reason_(reason), + error_(error) { + } + + FailureReason reason_; + GoogleServiceAuthError error_; +}; + // An interface that defines the callbacks for objects that the // Authenticator class will call to report the success/failure of // authentication for Chromium OS. class LoginStatusConsumer { public: virtual ~LoginStatusConsumer() {} - virtual void OnLoginFailure(const std::string& error) = 0; + virtual void OnLoginFailure(const LoginFailure& error) = 0; virtual void OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; virtual void OnOffTheRecordLoginSuccess() {} diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index d8a9fbf..30bed68 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -8,32 +8,33 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/lock.h" -#include "base/nss_util.h" #include "base/path_service.h" #include "base/scoped_ptr.h" #include "base/singleton.h" +#include "base/string_util.h" #include "base/time.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser_init.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/login_library.h" -#include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/external_cookie_handler.h" +#include "chrome/browser/chromeos/input_method/input_method_util.h" #include "chrome/browser/chromeos/login/cookie_fetcher.h" #include "chrome/browser/chromeos/login/google_authenticator.h" +#include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/login/user_image_downloader.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" -#include "chrome/common/logging_chrome.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/url_request_context_getter.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" -#include "chrome/common/notification_service.h" -#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "googleurl/src/gurl.h" #include "net/base/cookie_store.h" #include "net/url_request/url_request_context.h" @@ -43,7 +44,6 @@ namespace chromeos { namespace { -static char kIncognitoUser[] = "incognito"; // Prefix for Auth token received from ClientLogin request. const char kAuthPrefix[] = "Auth="; @@ -52,15 +52,10 @@ const char kAuthSuffix[] = "\n"; } // namespace -class LoginUtilsImpl : public LoginUtils, - public NotificationObserver { +class LoginUtilsImpl : public LoginUtils { public: LoginUtilsImpl() : browser_launch_enabled_(true) { - registrar_.Add( - this, - NotificationType::LOGIN_USER_CHANGED, - NotificationService::AllSources()); } // Invoked after the user has successfully logged in. This launches a browser @@ -70,7 +65,7 @@ class LoginUtilsImpl : public LoginUtils, // Invoked after the tmpfs is successfully mounted. // Launches a browser in the off the record (incognito) mode. - virtual void CompleteOffTheRecordLogin(); + virtual void CompleteOffTheRecordLogin(const GURL& start_url); // Creates and returns the authenticator to use. The caller owns the returned // Authenticator and must delete it when done. @@ -86,17 +81,7 @@ class LoginUtilsImpl : public LoginUtils, // Returns auth token for 'cp' Contacts service. virtual const std::string& GetAuthToken() const { return auth_token_; } - // NotificationObserver implementation. - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - private: - // Attempt to connect to the preferred network if available. - void ConnectToPreferredNetwork(); - - NotificationRegistrar registrar_; - // Indicates if DoBrowserLaunch will actually launch the browser or not. bool browser_launch_enabled_; @@ -137,8 +122,8 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, ""); + bool first_login = !UserManager::Get()->IsKnownUser(username); UserManager::Get()->UserLoggedIn(username); - ConnectToPreferredNetwork(); // Now launch the initial browser window. FilePath user_data_dir; @@ -153,8 +138,23 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, *(CommandLine::ForCurrentProcess()), logging::DELETE_OLD_LOG_FILE); - // Supply credentials for sync and others to use - profile->GetTokenService()->SetClientLoginResult(credentials); + // Supply credentials for sync and others to use. Load tokens from disk. + TokenService* token_service = profile->GetTokenService(); + token_service->Initialize(GaiaConstants::kChromeOSSource, + profile); + token_service->LoadTokensFromDB(); + token_service->UpdateCredentials(credentials); + if (token_service->AreCredentialsValid()) { + token_service->StartFetchingTokens(); + } + + // Set the CrOS user by getting this constructor run with the + // user's email on first retrieval. + profile->GetProfileSyncService(username); + + // Attempt to take ownership; this will fail if device is already owned. + OwnershipService::GetSharedInstance()->StartTakeOwnershipAttempt( + UserManager::Get()->logged_in_user().email()); // Take the credentials passed in and try to exchange them for // full-fledged Google authentication cookies. This is @@ -165,21 +165,77 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, CookieFetcher* cf = new CookieFetcher(profile); cf->AttemptFetch(credentials.data); auth_token_ = credentials.token; + + static const char kFallbackInputMethodLocale[] = "en-US"; + if (first_login) { + std::string locale(g_browser_process->GetApplicationLocale()); + // Add input methods based on the application locale when the user first + // logs in. For instance, if the user chooses Japanese as the UI + // language at the first login, we'll add input methods associated with + // Japanese, such as mozc. + if (locale != kFallbackInputMethodLocale) { + StringPrefMember language_preload_engines; + language_preload_engines.Init(prefs::kLanguagePreloadEngines, + profile->GetPrefs(), NULL); + StringPrefMember language_preferred_languages; + language_preferred_languages.Init(prefs::kLanguagePreferredLanguages, + profile->GetPrefs(), NULL); + + std::string preload_engines(language_preload_engines.GetValue()); + std::vector<std::string> input_method_ids; + input_method::GetInputMethodIdsFromLanguageCode( + locale, input_method::kAllInputMethods, &input_method_ids); + if (!input_method_ids.empty()) { + if (!preload_engines.empty()) + preload_engines += ','; + preload_engines += input_method_ids[0]; + } + language_preload_engines.SetValue(preload_engines); + + // Add the UI language to the preferred languages the user first logs in. + std::string preferred_languages(locale); + preferred_languages += ","; + preferred_languages += kFallbackInputMethodLocale; + language_preferred_languages.SetValue(preferred_languages); + } + } } -void LoginUtilsImpl::CompleteOffTheRecordLogin() { +void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) { LOG(INFO) << "Completing off the record login"; - if (CrosLibrary::Get()->EnsureLoaded()) - CrosLibrary::Get()->GetLoginLibrary()->StartSession(kIncognitoUser, ""); - - // Incognito flag is not set by default. - CommandLine::ForCurrentProcess()->AppendSwitch(switches::kIncognito); - UserManager::Get()->OffTheRecordUserLoggedIn(); - ConnectToPreferredNetwork(); - LoginUtils::DoBrowserLaunch( - ProfileManager::GetDefaultProfile()->GetOffTheRecordProfile()); + + if (CrosLibrary::Get()->EnsureLoaded()) { + // For BWSI we ask session manager to restart Chrome with --bwsi flag. + // We keep only some of the arguments of this process. + static const char* kForwardSwitches[] = { + switches::kLoggingLevel, + switches::kEnableLogging, + switches::kUserDataDir, + switches::kScrollPixels, + switches::kEnableGView, + switches::kNoFirstRun, + switches::kLoginProfile + }; + const CommandLine& browser_command_line = + *CommandLine::ForCurrentProcess(); + CommandLine command_line(browser_command_line.GetProgram()); + command_line.CopySwitchesFrom(browser_command_line, + kForwardSwitches, + arraysize(kForwardSwitches)); + command_line.AppendSwitch(switches::kBWSI); + command_line.AppendSwitch(switches::kIncognito); + command_line.AppendSwitch(switches::kEnableTabbedOptions); + command_line.AppendSwitchASCII( + switches::kLoginUser, + UserManager::Get()->logged_in_user().email()); + if (start_url.is_valid()) + command_line.AppendArg(start_url.spec()); + CrosLibrary::Get()->GetLoginLibrary()->RestartJob( + getpid(), + command_line.command_line_string()); + } } Authenticator* LoginUtilsImpl::CreateAuthenticator( @@ -195,18 +251,6 @@ bool LoginUtilsImpl::IsBrowserLaunchEnabled() const { return browser_launch_enabled_; } -void LoginUtilsImpl::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - if (type == NotificationType::LOGIN_USER_CHANGED) - base::OpenPersistentNSSDB(); -} - -void LoginUtilsImpl::ConnectToPreferredNetwork() { - CrosLibrary::Get()->GetNetworkLibrary()-> - ConnectToPreferredNetworkIfAvailable(); -} - LoginUtils* LoginUtils::Get() { return Singleton<LoginUtilsWrapper>::get()->get(); } @@ -229,23 +273,9 @@ void LoginUtils::DoBrowserLaunch(Profile* profile) { int return_code; browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(), profile, - std::wstring(), + FilePath(), true, &return_code); } -std::string LoginUtils::ExtractClientLoginParam( - const std::string& credentials, - const std::string& param_prefix, - const std::string& param_suffix) { - size_t start = credentials.find(param_prefix); - if (start == std::string::npos) - return std::string(); - start += param_prefix.size(); - size_t end = credentials.find(param_suffix, start); - if (end == std::string::npos) - return std::string(); - return credentials.substr(start, end - start); -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index 757fd21..b23c60d 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_UTILS_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_UTILS_H_ +#pragma once #include <string> -#include <vector> #include "chrome/common/net/gaia/gaia_auth_consumer.h" +class GURL; class Profile; namespace chromeos { @@ -30,13 +31,6 @@ class LoginUtils { // Task posted to the UI thread. static void DoBrowserLaunch(Profile* profile); - // Extracts specified param from given ClientLogin response. - // Returns the param value if found, empty string otherwise. - // Ex. prefix: "Auth=", suffix: "\n" - static std::string ExtractClientLoginParam(const std::string& credentials, - const std::string& param_prefix, - const std::string& param_suffix); - virtual ~LoginUtils() {} // Invoked after the user has successfully logged in. This launches a browser @@ -45,8 +39,9 @@ class LoginUtils { const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; // Invoked after the tmpfs is successfully mounted. - // Launches a browser in the off the record (incognito) mode. - virtual void CompleteOffTheRecordLogin() = 0; + // Asks session manager to restart Chrome in Browse Without Sign In mode. + // |start_url| is url for launched browser to open. + virtual void CompleteOffTheRecordLogin(const GURL& start_url) = 0; // Creates and returns the authenticator to use. The caller owns the returned // Authenticator and must delete it when done. diff --git a/chrome/browser/chromeos/login/message_bubble.cc b/chrome/browser/chromeos/login/message_bubble.cc index 3c69f89..82cb775 100644 --- a/chrome/browser/chromeos/login/message_bubble.cc +++ b/chrome/browser/chromeos/login/message_bubble.cc @@ -19,10 +19,17 @@ namespace chromeos { static const int kBorderSize = 4; static const int kMaxLabelWidth = 250; -MessageBubble::MessageBubble(views::WidgetGtk::Type type, views::Widget* parent, - SkBitmap* image, const std::wstring& text, bool grab_enabled) +MessageBubble::MessageBubble(views::WidgetGtk::Type type, + views::Widget* parent, + SkBitmap* image, + const std::wstring& text, + const std::wstring& help, + bool grab_enabled, + MessageBubbleDelegate* delegate) : InfoBubble(type), parent_(parent), + help_link_(NULL), + message_delegate_(delegate), grab_enabled_(grab_enabled) { using views::GridLayout; @@ -38,6 +45,12 @@ MessageBubble::MessageBubble(views::WidgetGtk::Type type, views::Widget* parent, column_set->AddPaddingColumn(0, kBorderSize); column_set->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); + if (!help.empty()) { + column_set = layout->AddColumnSet(1); + column_set->AddPaddingColumn(0, kBorderSize + image->width()); + column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, + GridLayout::USE_PREF, 0, 0); + } ResourceBundle& rb = ResourceBundle::GetSharedInstance(); @@ -60,6 +73,13 @@ MessageBubble::MessageBubble(views::WidgetGtk::Type type, views::Widget* parent, close_button_->SetImage(views::CustomButton::BS_PUSHED, rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); layout->AddView(close_button_); + + if (!help.empty()) { + layout->StartRowWithPadding(0, 1, 0, kBorderSize); + help_link_ = new views::Link(help); + help_link_->SetController(this); + layout->AddView(help_link_); + } } void MessageBubble::ButtonPressed(views::Button* sender, @@ -71,16 +91,26 @@ void MessageBubble::ButtonPressed(views::Button* sender, } } +void MessageBubble::LinkActivated(views::Link* source, int event_flags) { + if (source == help_link_) { + if (message_delegate_) + message_delegate_->OnHelpLinkActivated(); + } else { + NOTREACHED() << "Unknown view"; + } +} + // static MessageBubble* MessageBubble::Show(views::Widget* parent, const gfx::Rect& position_relative_to, BubbleBorder::ArrowLocation arrow_location, SkBitmap* image, const std::wstring& text, - InfoBubbleDelegate* delegate) { + const std::wstring& help, + MessageBubbleDelegate* delegate) { // The bubble will be destroyed when it is closed. MessageBubble* bubble = new MessageBubble( - views::WidgetGtk::TYPE_WINDOW, parent, image, text, true); + views::WidgetGtk::TYPE_WINDOW, parent, image, text, help, true, delegate); bubble->Init(parent, position_relative_to, arrow_location, bubble->text_->GetParent(), delegate); return bubble; @@ -93,10 +123,11 @@ MessageBubble* MessageBubble::ShowNoGrab( BubbleBorder::ArrowLocation arrow_location, SkBitmap* image, const std::wstring& text, - InfoBubbleDelegate* delegate) { + const std::wstring& help, + MessageBubbleDelegate* delegate) { // The bubble will be destroyed when it is closed. MessageBubble* bubble = new MessageBubble( - views::WidgetGtk::TYPE_CHILD, parent, image, text, false); + views::WidgetGtk::TYPE_CHILD, parent, image, text, help, false, delegate); bubble->Init(parent, position_relative_to, arrow_location, bubble->text_->GetParent(), delegate); return bubble; diff --git a/chrome/browser/chromeos/login/message_bubble.h b/chrome/browser/chromeos/login/message_bubble.h index d265ac1..f29ecf8 100644 --- a/chrome/browser/chromeos/login/message_bubble.h +++ b/chrome/browser/chromeos/login/message_bubble.h @@ -4,9 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MESSAGE_BUBBLE_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_MESSAGE_BUBBLE_H_ +#pragma once #include "chrome/browser/views/info_bubble.h" #include "views/controls/button/button.h" +#include "views/controls/link.h" #include "views/view.h" #include "views/widget/widget_gtk.h" @@ -20,9 +22,16 @@ class Label; namespace chromeos { +class MessageBubbleDelegate : public InfoBubbleDelegate { + public: + // Called when the user clicked on help link. + virtual void OnHelpLinkActivated() = 0; +}; + // MessageBubble is used to show error and info messages on OOBE screens. class MessageBubble : public InfoBubble, - public views::ButtonListener { + public views::ButtonListener, + public views::LinkController { public: // Create and show bubble. position_relative_to must be in screen coordinates. static MessageBubble* Show(views::Widget* parent, @@ -30,7 +39,8 @@ class MessageBubble : public InfoBubble, BubbleBorder::ArrowLocation arrow_location, SkBitmap* image, const std::wstring& text, - InfoBubbleDelegate* delegate); + const std::wstring& help, + MessageBubbleDelegate* delegate); // Create and show bubble which does not grab pointer. This creates // a TYPE_CHILD WidgetGtk and |position_relative_to| must be in parent's @@ -40,7 +50,8 @@ class MessageBubble : public InfoBubble, BubbleBorder::ArrowLocation arrow_location, SkBitmap* image, const std::wstring& text, - InfoBubbleDelegate* delegate); + const std::wstring& help, + MessageBubbleDelegate* delegate); // Overridden from WidgetGtk. virtual void Close(); @@ -52,23 +63,32 @@ class MessageBubble : public InfoBubble, } protected: - // views::ButtonListener implmenets. + // Overridden from views::ButtonListener: virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // Overridden from views::LinkController: + virtual void LinkActivated(views::Link* source, int event_flags); + // Overridden from WidgetGtk. virtual void IsActiveChanged(); virtual void DoGrab(); private: MessageBubble(views::WidgetGtk::Type type, - views::Widget* parent, SkBitmap* image, - const std::wstring& text, bool grab_enabled); + views::Widget* parent, + SkBitmap* image, + const std::wstring& text, + const std::wstring& help, + bool grab_enabled, + MessageBubbleDelegate* delegate); views::Widget* parent_; views::ImageView* icon_; views::Label* text_; views::ImageButton* close_button_; + views::Link* help_link_; + MessageBubbleDelegate* message_delegate_; bool grab_enabled_; DISALLOW_COPY_AND_ASSIGN(MessageBubble); diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.h b/chrome/browser/chromeos/login/mock_auth_response_handler.h index 81c33b2..db4651a 100644 --- a/chrome/browser/chromeos/login/mock_auth_response_handler.h +++ b/chrome/browser/chromeos/login/mock_auth_response_handler.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTH_RESPONSE_HANDLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTH_RESPONSE_HANDLER_H_ +#pragma once #include "chrome/browser/chromeos/login/auth_response_handler.h" diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h index 788708a..31a8542 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.h +++ b/chrome/browser/chromeos/login/mock_authenticator.h @@ -4,11 +4,14 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTHENTICATOR_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTHENTICATOR_H_ +#pragma once #include <string> +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/login_utils.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" #include "testing/gtest/include/gtest/gtest.h" class Profile; @@ -43,11 +46,13 @@ class MockAuthenticator : public Authenticator { GaiaAuthConsumer::ClientLoginResult())); return true; } else { + GoogleServiceAuthError error( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &MockAuthenticator::OnLoginFailure, - std::string("Login failed"))); + LoginFailure::FromNetworkAuthFailure(error))); return false; } } @@ -68,8 +73,8 @@ class MockAuthenticator : public Authenticator { consumer_->OnLoginSuccess(expected_username_, credentials); } - void OnLoginFailure(const std::string& data) { - consumer_->OnLoginFailure(data); + void OnLoginFailure(const LoginFailure& failure) { + consumer_->OnLoginFailure(failure); LOG(INFO) << "Posting a QuitTask to UI thread"; ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, new MessageLoop::QuitTask); @@ -106,7 +111,7 @@ class MockLoginUtils : public LoginUtils { EXPECT_EQ(expected_username_, username); } - virtual void CompleteOffTheRecordLogin() { + virtual void CompleteOffTheRecordLogin(const GURL& start_url) { } virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer) { diff --git a/chrome/browser/chromeos/login/mock_screen_observer.h b/chrome/browser/chromeos/login/mock_screen_observer.h index 1df4106..951c045 100644 --- a/chrome/browser/chromeos/login/mock_screen_observer.h +++ b/chrome/browser/chromeos/login/mock_screen_observer.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_SCREEN_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_SCREEN_OBSERVER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/mock_update_screen.h b/chrome/browser/chromeos/login/mock_update_screen.h index 2e6beb2..ad34cb8 100644 --- a/chrome/browser/chromeos/login/mock_update_screen.h +++ b/chrome/browser/chromeos/login/mock_update_screen.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_UPDATE_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_UPDATE_SCREEN_H_ +#pragma once #include "chrome/browser/chromeos/login/update_screen.h" #include "chrome/browser/chromeos/login/view_screen.h" diff --git a/chrome/browser/chromeos/login/network_screen.cc b/chrome/browser/chromeos/login/network_screen.cc index 2851966..9b4414d 100644 --- a/chrome/browser/chromeos/login/network_screen.cc +++ b/chrome/browser/chromeos/login/network_screen.cc @@ -5,12 +5,17 @@ #include "chrome/browser/chromeos/login/network_screen.h" #include "app/l10n_util.h" -#include "base/utf_string_conversions.h" +#include "app/resource_bundle.h" #include "base/logging.h" +#include "base/string16.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/login/help_app_launcher.h" #include "chrome/browser/chromeos/login/network_selection_view.h" #include "chrome/browser/chromeos/login/screen_observer.h" +#include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" #include "views/widget/widget.h" #include "views/window/window.h" @@ -20,6 +25,10 @@ namespace { // Time in seconds for connection timeout. const int kConnectionTimeoutSec = 15; +// Considering 10px shadow from each side & welcome title height at 30px. +const int kWelcomeScreenWidth = 580; +const int kWelcomeScreenHeight = 335; + } // namespace namespace chromeos { @@ -27,15 +36,13 @@ namespace chromeos { /////////////////////////////////////////////////////////////////////////////// // NetworkScreen, public: -NetworkScreen::NetworkScreen(WizardScreenDelegate* delegate, bool is_out_of_box) - : ViewScreen<NetworkSelectionView>(delegate), +NetworkScreen::NetworkScreen(WizardScreenDelegate* delegate) + : ViewScreen<NetworkSelectionView>(delegate, + kWelcomeScreenWidth, + kWelcomeScreenHeight), is_network_subscribed_(false), - wifi_disabled_(false), - is_out_of_box_(is_out_of_box), - is_waiting_for_connect_(false), continue_pressed_(false), - ethernet_preselected_(false), - ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { + bubble_(NULL) { } NetworkScreen::~NetworkScreen() { @@ -44,60 +51,12 @@ NetworkScreen::~NetworkScreen() { } //////////////////////////////////////////////////////////////////////////////// -// ComboboxModel implementation: - -int NetworkScreen::GetItemCount() { - // Item with index = 0 is either "no networks are available" or - // "no selection". - // If WiFi is disabled adding extra item to enable it. - return static_cast<int>(networks_.GetNetworkCount()) + 1 + - (wifi_disabled_ ? 1 : 0); -} - -std::wstring NetworkScreen::GetItemAt(int index) { - if (index == 0) { - return networks_.IsEmpty() ? - l10n_util::GetString(IDS_STATUSBAR_NO_NETWORKS_MESSAGE) : - l10n_util::GetString(IDS_NETWORK_SELECTION_NONE); - } - if (wifi_disabled_ && - index == static_cast<int>(networks_.GetNetworkCount()) + 1) { - return l10n_util::GetStringF( - IDS_STATUSBAR_NETWORK_DEVICE_ENABLE, - l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_WIFI)); - } - NetworkList::NetworkItem* network = - networks_.GetNetworkAt(index - 1); - return network ? UTF16ToWide(network->label) : std::wstring(); -} - -//////////////////////////////////////////////////////////////////////////////// -// views::Combobox::Listener implementation: - -void NetworkScreen::ItemChanged(views::Combobox* sender, - int prev_index, - int new_index) { - view()->EnableContinue(new_index > 0); - // Corner case: item with index 0 is "No selection". Just select it. - if (new_index == prev_index || new_index <= 0 || prev_index < 0) - return; - - if (wifi_disabled_ && - new_index == static_cast<int>(networks_.GetNetworkCount()) + 1) { - view()->EnableContinue(false); - MessageLoop::current()->PostTask(FROM_HERE, - task_factory_.NewRunnableMethod(&NetworkScreen::EnableWiFi)); - return; - } - - if (networks_.IsEmpty()) - return; +// NetworkScreen, NetworkScreenDelegate implementation: - // Connect to network as early as possible. - const NetworkList::NetworkItem* network = - networks_.GetNetworkAt(new_index - 1); - MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod( - &NetworkScreen::ConnectToNetwork, network->network_type, network->label)); +void NetworkScreen::ClearErrors() { + // bubble_ will be set to NULL in callback. + if (bubble_) + bubble_->Close(); } /////////////////////////////////////////////////////////////////////////////// @@ -105,24 +64,13 @@ void NetworkScreen::ItemChanged(views::Combobox* sender, void NetworkScreen::ButtonPressed(views::Button* sender, const views::Event& event) { - // Proceed only when selected network is connected. - const NetworkList::NetworkItem* network = GetSelectedNetwork(); - if (!network) - return; - if (networks_.IsNetworkConnected(network->network_type, network->label)) { - MessageLoop::current()->PostTask(FROM_HERE, - task_factory_.NewRunnableMethod(&NetworkScreen::NotifyOnConnection)); + ClearErrors(); + NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary(); + if (network && network->Connected()) { + NotifyOnConnection(); } else { continue_pressed_ = true; - if (is_waiting_for_connect_) { - ShowConnectingStatus(); - } else { - MessageLoop::current()->PostTask( - FROM_HERE, - task_factory_.NewRunnableMethod(&NetworkScreen::ConnectToNetwork, - network->network_type, - network->label)); - } + WaitForConnection(network_id_); } } @@ -130,58 +78,7 @@ void NetworkScreen::ButtonPressed(views::Button* sender, // NetworkLibrary::Observer implementation: void NetworkScreen::NetworkChanged(NetworkLibrary* network_lib) { - if (!view()) - return; - - // TODO(nkostylev): Reuse network menu button - http://crosbug.com/4133 - wifi_disabled_ = !chromeos::CrosLibrary::Get()-> - GetNetworkLibrary()->wifi_enabled(); - - // Save network selection in case it would be available after refresh. - NetworkList::NetworkType network_type = NetworkList::NETWORK_EMPTY; - string16 network_id; - const NetworkList::NetworkItem* selected_network = GetSelectedNetwork(); - if (selected_network) { - network_type = selected_network->network_type; - network_id = selected_network->label; - } - networks_.NetworkChanged(network_lib); - if (is_waiting_for_connect_ && - networks_.IsNetworkConnected(connecting_network_.network_type, - connecting_network_.label)) { - // Stop waiting & don't update spinner status. - StopWaitingForConnection(false); - if (continue_pressed_) { - MessageLoop::current()->PostTask(FROM_HERE, - task_factory_.NewRunnableMethod(&NetworkScreen::NotifyOnConnection)); - return; - } - } - view()->NetworkModelChanged(); - // Prefer Ethernet when it's connected (only once). - if (!ethernet_preselected_ && - networks_.IsNetworkConnected(NetworkList::NETWORK_ETHERNET, string16())) { - ethernet_preselected_ = true; - SelectNetwork(NetworkList::NETWORK_ETHERNET, string16()); - } else { - SelectNetwork(network_type, network_id); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// NetworkLibrary::Observer implementation: - -void NetworkScreen::OnDialogAccepted() { - const NetworkList::NetworkItem* network = GetSelectedNetwork(); - if (network) - WaitForConnection(network); -} - -void NetworkScreen::OnDialogCancelled() { - if (view()) { - view()->EnableContinue(false); - view()->SetSelectedNetworkItem(0); - } + UpdateStatus(network_lib); } /////////////////////////////////////////////////////////////////////////////// @@ -196,6 +93,15 @@ NetworkSelectionView* NetworkScreen::AllocateView() { return new NetworkSelectionView(this); } +/////////////////////////////////////////////////////////////////////////////// +// NetworkScreen, views::InfoBubbleDelegate implementation: + +void NetworkScreen::OnHelpLinkActivated() { + if (!help_app_.get()) + help_app_.reset(new HelpAppLauncher(view()->GetNativeWindow())); + help_app_->ShowHelpTopic(HelpAppLauncher::HELP_CONNECTIVITY); +} + //////////////////////////////////////////////////////////////////////////////// // NetworkScreen, public: @@ -209,37 +115,6 @@ void NetworkScreen::Refresh() { //////////////////////////////////////////////////////////////////////////////// // NetworkScreen, private: -void NetworkScreen::ConnectToNetwork(NetworkList::NetworkType type, - const string16& id) { - const NetworkList::NetworkItem* network = - networks_.GetNetworkById(type, id); - if (network && - !networks_.IsNetworkConnected(type, id)) { - if (NetworkList::NETWORK_WIFI == network->network_type) { - if (network->wifi_network.encrypted()) { - OpenPasswordDialog(network->wifi_network); - return; - } else { - WaitForConnection(network); - chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> - ConnectToWifiNetwork(network->wifi_network, - std::string(), std::string(), std::string()); - } - } else if (NetworkList::NETWORK_CELLULAR == network->network_type) { - WaitForConnection(network); - chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> - ConnectToCellularNetwork(network->cellular_network); - } - } -} - -void NetworkScreen::EnableWiFi() { - if (CrosLibrary::Get()->EnsureLoaded()) { - chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> - EnableWifiNetworkDevice(true); - } -} - void NetworkScreen::SubscribeNetworkNotification() { if (!is_network_subscribed_) { is_network_subscribed_ = true; @@ -254,13 +129,6 @@ void NetworkScreen::UnsubscribeNetworkNotification() { } } -NetworkList::NetworkItem* NetworkScreen::GetSelectedNetwork() { - if (!view()) - return NULL; - - return networks_.GetNetworkAt(view()->GetSelectedNetworkItem() - 1); -} - void NetworkScreen::NotifyOnConnection() { // TODO(nkostylev): Check network connectivity. UnsubscribeNetworkNotification(); @@ -269,62 +137,71 @@ void NetworkScreen::NotifyOnConnection() { } void NetworkScreen::OnConnectionTimeout() { - continue_pressed_ = false; - // TODO(nkostylev): Notify on connection error. - if (is_waiting_for_connect_) { - // Stop waiting & show selection combobox. - StopWaitingForConnection(true); - } -} - -void NetworkScreen::OpenPasswordDialog(WifiNetwork network) { - NetworkConfigView* dialog = new NetworkConfigView(network, true); - dialog->set_delegate(this); - dialog->set_browser_mode(false); - views::Window* window = views::Window::CreateChromeWindow( - view()->GetNativeWindow(), gfx::Rect(), dialog); - window->SetIsAlwaysOnTop(true); - window->Show(); - dialog->SetLoginTextfieldFocus(); -} + StopWaitingForConnection(network_id_); + // Show error bubble. + ClearErrors(); + views::View* network_control = view()->GetNetworkControlView(); + bubble_ = MessageBubble::Show( + network_control->GetWidget(), + network_control->GetScreenBounds(), + BubbleBorder::LEFT_TOP, + ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), + l10n_util::GetStringF(IDS_NETWORK_SELECTION_ERROR, + l10n_util::GetString(IDS_PRODUCT_OS_NAME), + UTF16ToWide(network_id_)), + l10n_util::GetString(IDS_NETWORK_SELECTION_ERROR_HELP), + this); +} + +void NetworkScreen::UpdateStatus(NetworkLibrary* network) { + if (!view() || !network) + return; -void NetworkScreen::SelectNetwork(NetworkList::NetworkType type, - const string16& id) { - int index = networks_.GetNetworkIndexById(type, id); - if (index >= 0) { - view()->SetSelectedNetworkItem(index + 1); + if (network->ethernet_connected()) { + StopWaitingForConnection( + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); + } else if (network->wifi_connected()) { + StopWaitingForConnection(ASCIIToUTF16(network->wifi_name())); + } else if (network->cellular_connected()) { + StopWaitingForConnection(ASCIIToUTF16(network->cellular_name())); + } else if (network->ethernet_connecting()) { + WaitForConnection( + l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)); + } else if (network->wifi_connecting()) { + WaitForConnection(ASCIIToUTF16(network->wifi_name())); + } else if (network->cellular_connecting()) { + WaitForConnection(ASCIIToUTF16(network->cellular_name())); } else { - view()->SetSelectedNetworkItem(0); + view()->EnableContinue(network->Connected()); } } -void NetworkScreen::ShowConnectingStatus() { - view()->ShowConnectingStatus(is_waiting_for_connect_, - connecting_network_.label); -} +void NetworkScreen::StopWaitingForConnection(const string16& network_id) { + NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary(); + bool is_connected = network && network->Connected(); + if (is_connected && continue_pressed_) { + NotifyOnConnection(); + return; + } + + continue_pressed_ = false; + connection_timer_.Stop(); -void NetworkScreen::StopWaitingForConnection(bool show_combobox) { - if (connection_timer_.IsRunning()) - connection_timer_.Stop(); - is_waiting_for_connect_ = false; - connecting_network_.network_type = NetworkList::NETWORK_EMPTY; - connecting_network_.label.clear(); - if (show_combobox) - ShowConnectingStatus(); + network_id_ = network_id; + view()->ShowConnectingStatus(false, network_id_); + view()->EnableContinue(is_connected); } -void NetworkScreen::WaitForConnection(const NetworkList::NetworkItem* network) { - is_waiting_for_connect_ = true; - DCHECK(network); - connecting_network_.network_type = network->network_type; - connecting_network_.label = network->label; - if (connection_timer_.IsRunning()) - connection_timer_.Stop(); +void NetworkScreen::WaitForConnection(const string16& network_id) { + connection_timer_.Stop(); connection_timer_.Start(base::TimeDelta::FromSeconds(kConnectionTimeoutSec), this, &NetworkScreen::OnConnectionTimeout); - if (continue_pressed_) - ShowConnectingStatus(); + + network_id_ = network_id; + view()->ShowConnectingStatus(true, network_id_); + + view()->EnableContinue(false); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/network_screen.h b/chrome/browser/chromeos/login/network_screen.h index e7d00cb..860e901 100644 --- a/chrome/browser/chromeos/login/network_screen.h +++ b/chrome/browser/chromeos/login/network_screen.h @@ -4,14 +4,16 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SCREEN_H_ +#pragma once -#include <string> - +#include "base/scoped_ptr.h" +#include "base/string16.h" #include "base/task.h" #include "base/timer.h" #include "chrome/browser/chromeos/cros/network_library.h" -#include "chrome/browser/chromeos/login/network_screen_delegate.h" #include "chrome/browser/chromeos/login/language_switch_menu.h" +#include "chrome/browser/chromeos/login/message_bubble.h" +#include "chrome/browser/chromeos/login/network_screen_delegate.h" #include "chrome/browser/chromeos/login/view_screen.h" #include "chrome/browser/chromeos/network_list.h" #include "chrome/browser/chromeos/options/network_config_view.h" @@ -20,39 +22,28 @@ class WizardScreenDelegate; namespace chromeos { +class HelpAppLauncher; class NetworkSelectionView; class NetworkScreen : public ViewScreen<NetworkSelectionView>, - public NetworkScreenDelegate, - public NetworkConfigView::Delegate { + public MessageBubbleDelegate, + public NetworkScreenDelegate { public: - NetworkScreen(WizardScreenDelegate* delegate, bool is_out_of_box); + explicit NetworkScreen(WizardScreenDelegate* delegate); virtual ~NetworkScreen(); // NetworkScreenDelegate implementation: + virtual void ClearErrors(); virtual LanguageSwitchMenu* language_switch_menu() { return &language_switch_menu_; } - - // ComboboxModel implementation: - virtual int GetItemCount(); - virtual std::wstring GetItemAt(int index); - - // views::Combobox::Listener implementation: - virtual void ItemChanged(views::Combobox* sender, - int prev_index, - int new_index); + virtual gfx::Size size() const { return GetScreenSize(); } // views::ButtonListener implementation: virtual void ButtonPressed(views::Button* sender, const views::Event& event); // NetworkLibrary::Observer implementation: virtual void NetworkChanged(NetworkLibrary* network_lib); - virtual void NetworkTraffic(NetworkLibrary* cros, int traffic_type) {} - - // NetworkConfigView::Delegate implementation: - virtual void OnDialogAccepted(); - virtual void OnDialogCancelled(); protected: // Subscribes NetworkScreen to the network change notification, @@ -60,15 +51,18 @@ class NetworkScreen : public ViewScreen<NetworkSelectionView>, void Refresh(); private: + FRIEND_TEST(NetworkScreenTest, Timeout); + // ViewScreen implementation: virtual void CreateView(); virtual NetworkSelectionView* AllocateView(); - // Connects to network if needed and updates screen state. - void ConnectToNetwork(NetworkList::NetworkType type, const string16& id); - - // Enables WiFi device. - void EnableWiFi(); + // Overridden from views::InfoBubbleDelegate. + virtual void InfoBubbleClosing(InfoBubble* info_bubble, + bool closed_by_escape) { bubble_ = NULL; } + virtual bool CloseOnEscape() { return true; } + virtual bool FadeInOnShow() { return false; } + virtual void OnHelpLinkActivated(); // Subscribes to network change notifications. void SubscribeNetworkNotification(); @@ -76,63 +70,43 @@ class NetworkScreen : public ViewScreen<NetworkSelectionView>, // Unsubscribes from network change notifications. void UnsubscribeNetworkNotification(); - // Returns currently selected network in the combobox. - NetworkList::NetworkItem* GetSelectedNetwork(); - // Notifies wizard on successful connection. void NotifyOnConnection(); // Called by |connection_timer_| when connection to the network timed out. void OnConnectionTimeout(); - // Opens password dialog for the encrypted networks. - void OpenPasswordDialog(WifiNetwork network); - - // Selects network by type and id. - void SelectNetwork(NetworkList::NetworkType type, - const string16& id); - - // Switches connecting status based on |is_waiting_for_connect_|. - void ShowConnectingStatus(); + // Update UI based on current network status. + void UpdateStatus(NetworkLibrary* network); // Stops waiting for network to connect. - // If |show_combobox| is false, spinner is left on screen. Used on exit. - void StopWaitingForConnection(bool show_combobox); + void StopWaitingForConnection(const string16& network_id); // Starts waiting for network connection. Shows spinner. - void WaitForConnection(const NetworkList::NetworkItem* network); + void WaitForConnection(const string16& network_id); // True if subscribed to network change notification. bool is_network_subscribed_; - // Networks model, contains current state of available networks. - NetworkList networks_; - - // True if WiFi is currently disabled. - bool wifi_disabled_; - - // True if full OOBE flow should be shown. - bool is_out_of_box_; - - // True if we're waiting for the selected network being connected. - bool is_waiting_for_connect_; + // ID of the the network that we are waiting for. + string16 network_id_; - // True if "Continue" button was pressed. - // Set only when there's a network selected. + // True if user pressed continue button so we should proceed with OOBE + // as soon as we are connected. bool continue_pressed_; - // True if Ethernet was already preselected in combobox. - bool ethernet_preselected_; - // Timer for connection timeout. base::OneShotTimer<NetworkScreen> connection_timer_; - // Network which we're connecting to. - NetworkList::NetworkItem connecting_network_; - - ScopedRunnableMethodFactory<NetworkScreen> task_factory_; LanguageSwitchMenu language_switch_menu_; + // Pointer to shown message bubble. We don't need to delete it because + // it will be deleted on bubble closing. + MessageBubble* bubble_; + + // Help application used for help dialogs. + scoped_ptr<HelpAppLauncher> help_app_; + DISALLOW_COPY_AND_ASSIGN(NetworkScreen); }; diff --git a/chrome/browser/chromeos/login/network_screen_browsertest.cc b/chrome/browser/chromeos/login/network_screen_browsertest.cc index e31bac8..43fa5b7 100644 --- a/chrome/browser/chromeos/login/network_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/network_screen_browsertest.cc @@ -8,22 +8,24 @@ #include "app/l10n_util.h" #include "base/message_loop.h" #include "base/scoped_ptr.h" -#include "base/string_util.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/mock_login_library.h" #include "chrome/browser/chromeos/cros/mock_network_library.h" #include "chrome/browser/chromeos/cros/network_library.h" -#include "chrome/browser/chromeos/login/network_selection_view.h" -#include "chrome/browser/chromeos/login/network_screen.h" #include "chrome/browser/chromeos/login/mock_screen_observer.h" +#include "chrome/browser/chromeos/login/network_screen.h" +#include "chrome/browser/chromeos/login/network_selection_view.h" #include "chrome/browser/chromeos/login/view_screen.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_in_process_browser_test.h" #include "chrome/browser/chromeos/login/wizard_screen.h" #include "chrome/test/ui_test_utils.h" #include "grit/generated_resources.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" #include "views/controls/button/text_button.h" #include "views/controls/combobox/combobox.h" @@ -42,28 +44,19 @@ class DummyButtonListener : public views::ButtonListener { const views::Event& event) {} }; -class DummyComboboxModel : public ComboboxModel { - public: - virtual int GetItemCount() { return 2; } - - virtual std::wstring GetItemAt(int index) { - return L"Item " + IntToWString(index); - } -}; - class NetworkScreenTest : public WizardInProcessBrowserTest { public: - NetworkScreenTest(): WizardInProcessBrowserTest("network") { - cellular_.set_name("Cellular network"); - wifi_.set_name("WiFi network"); + NetworkScreenTest(): WizardInProcessBrowserTest("network"), + mock_login_library_(NULL), + mock_network_library_(NULL) { } protected: virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - + cros_mock_->InitStatusAreaMocks(); + mock_network_library_ = cros_mock_->mock_network_library(); mock_login_library_ = new MockLoginLibrary(); - test_api()->SetLoginLibrary(mock_login_library_, true); + cros_mock_->test_api()->SetLoginLibrary(mock_login_library_, true); EXPECT_CALL(*mock_login_library_, EmitLoginPromptReady()) .Times(1); @@ -71,99 +64,40 @@ class NetworkScreenTest : public WizardInProcessBrowserTest { // Status bar expectations are defined with RetiresOnSaturation() so // these mocks will be active once status bar is initialized. EXPECT_CALL(*mock_network_library_, ethernet_connected()) - .Times(1) - .WillOnce((Return(false))); - EXPECT_CALL(*mock_network_library_, ethernet_connecting()) - .Times(1) - .WillOnce((Return(false))); - EXPECT_CALL(*mock_network_library_, wifi_enabled()) - .Times(1) - .WillOnce((Return(true))); - EXPECT_CALL(*mock_network_library_, wifi_networks()) - .Times(1) - .WillOnce((ReturnRef(wifi_networks_))); - EXPECT_CALL(*mock_network_library_, cellular_networks()) - .Times(1) - .WillOnce((ReturnRef(cellular_networks_))); - EXPECT_CALL(*mock_network_library_, AddObserver(_)) - .Times(1); - EXPECT_CALL(*mock_network_library_, RemoveObserver(_)) - .Times(1); - - SetStatusAreaMocksExpectations(); - } - - virtual void TearDownInProcessBrowserTestFixture() { - CrosInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); - test_api()->SetLoginLibrary(NULL, false); - } - - void NetworkChangedExpectations(bool wifi_enabled) { - EXPECT_CALL(*mock_network_library_, wifi_enabled()) - .Times(1) - .WillOnce((Return(wifi_enabled))); - } - - void EthernetExpectations(bool connected, bool connecting) { - EXPECT_CALL(*mock_network_library_, ethernet_connected()) - .Times(1) - .WillRepeatedly((Return(connected))); + .Times(3) + .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, ethernet_connecting()) .Times(1) - .WillRepeatedly((Return(connecting))); - } - - void WifiExpectations(bool connected, bool connecting) { + .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, wifi_connected()) - .Times(1) - .WillOnce((Return(connected))); + .Times(3) + .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, wifi_connecting()) - .Times(1) - .WillOnce((Return(connecting))); - } - - void SetupWifiNetwork(bool connected, bool connecting) { - wifi_networks_.clear(); - wifi_.set_connected(connected); - wifi_.set_connecting(connecting); - wifi_networks_.push_back(wifi_); - } - - void SetupCellularNetwork(bool connected, bool connecting) { - cellular_networks_.clear(); - cellular_.set_connected(connected); - cellular_.set_connecting(connecting); - cellular_networks_.push_back(cellular_); - } - - void CellularExpectations(bool connected, bool connecting) { + .Times(3) + .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, cellular_connected()) - .Times(1) - .WillOnce((Return(connected))); + .Times(3) + .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_network_library_, cellular_connecting()) - .Times(1) - .WillOnce((Return(connecting))); - } - - void WifiCellularNetworksExpectations() { - EXPECT_CALL(*mock_network_library_, wifi_networks()) - .Times(1) - .WillOnce((ReturnRef(wifi_networks_))); - EXPECT_CALL(*mock_network_library_, cellular_networks()) - .Times(1) - .WillOnce((ReturnRef(cellular_networks_))); - } + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mock_network_library_, Connected()) + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mock_network_library_, Connecting()) + .Times(2) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mock_network_library_, AddObserver(_)) + .Times(2); + EXPECT_CALL(*mock_network_library_, RemoveObserver(_)) + .Times(2); - void WifiSsidExpectation(const std::string& ssid) { - EXPECT_CALL(*mock_network_library_, wifi_name()) - .Times(1) - .WillOnce((ReturnRef(ssid))); + cros_mock_->SetStatusAreaMocksExpectations(); } - void CellularNameExpectation(const std::string& name) { - EXPECT_CALL(*mock_network_library_, cellular_name()) - .Times(1) - .WillOnce((ReturnRef(name))); + virtual void TearDownInProcessBrowserTestFixture() { + CrosInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); + cros_mock_->test_api()->SetLoginLibrary(NULL, false); } void EmulateContinueButtonExit(NetworkScreen* network_screen) { @@ -172,6 +106,8 @@ class NetworkScreenTest : public WizardInProcessBrowserTest { EXPECT_CALL(*mock_screen_observer, OnExit(ScreenObserver::NETWORK_CONNECTED)) .Times(1); + EXPECT_CALL(*mock_network_library_, Connected()) + .WillOnce(Return(true)); controller()->set_observer(mock_screen_observer.get()); DummyButtonListener button_listener; views::TextButton button(&button_listener, L"Button"); @@ -184,18 +120,13 @@ class NetworkScreenTest : public WizardInProcessBrowserTest { } MockLoginLibrary* mock_login_library_; - - CellularNetworkVector cellular_networks_; - WifiNetworkVector wifi_networks_; - - CellularNetwork cellular_; - WifiNetwork wifi_; + MockNetworkLibrary* mock_network_library_; private: DISALLOW_COPY_AND_ASSIGN(NetworkScreenTest); }; -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Basic) { +IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Ethernet) { ASSERT_TRUE(controller()); NetworkScreen* network_screen = controller()->GetNetworkScreen(); ASSERT_TRUE(network_screen != NULL); @@ -203,328 +134,148 @@ IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Basic) { NetworkSelectionView* network_view = network_screen->view(); ASSERT_TRUE(network_view != NULL); - ASSERT_EQ(1, network_screen->GetItemCount()); - EXPECT_EQ(l10n_util::GetString(IDS_STATUSBAR_NO_NETWORKS_MESSAGE), - network_screen->GetItemAt(0)); -} + EXPECT_FALSE(network_view->IsContinueEnabled()); -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, EnableWifi) { - ASSERT_TRUE(controller()); - NetworkScreen* network_screen = controller()->GetNetworkScreen(); - ASSERT_TRUE(network_screen != NULL); - ASSERT_EQ(network_screen, controller()->current_screen()); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - - // WiFi is disabled. - NetworkChangedExpectations(false); - EthernetExpectations(false, false); - WifiCellularNetworksExpectations(); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(l10n_util::GetString(IDS_STATUSBAR_NO_NETWORKS_MESSAGE), - network_screen->GetItemAt(0)); - EXPECT_EQ(l10n_util::GetStringF(IDS_STATUSBAR_NETWORK_DEVICE_ENABLE, - l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_WIFI)), - network_screen->GetItemAt(1)); - - // Emulate "Enable Wifi" item press. - EXPECT_CALL(*mock_network_library_, EnableWifiNetworkDevice(true)) - .Times(1); - DummyComboboxModel combobox_model; - views::Combobox combobox(&combobox_model); - network_screen->ItemChanged(&combobox, 0, 1); - network_view->SetSelectedNetworkItem(1); - ui_test_utils::RunAllPendingInMessageLoop(); - ASSERT_EQ(network_screen, controller()->current_screen()); -} + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, cellular_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, ethernet_connecting()) + .WillOnce((Return(true))); -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, NetworksConnectedNotSelected) { - ASSERT_TRUE(controller()); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - NetworkScreen* network_screen = controller()->GetNetworkScreen(); - ASSERT_TRUE(network_screen != NULL); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); + network_screen->NetworkChanged(mock_network_library_); + EXPECT_FALSE(network_view->IsContinueEnabled()); + EXPECT_TRUE(network_view->IsConnecting()); - EthernetExpectations(true, false); - WifiCellularNetworksExpectations(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - // Ethernet is preselected once. - EXPECT_EQ(1, network_view->GetSelectedNetworkItem()); - ASSERT_EQ(network_screen, controller()->current_screen()); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), - network_screen->GetItemAt(1)); - - // Ethernet - disconnected, WiFi & Cellular - connected. - EthernetExpectations(false, false); - SetupWifiNetwork(true, false); - WifiExpectations(true, false); - SetupCellularNetwork(true, false); - CellularExpectations(true, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - CellularNameExpectation(cellular_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(network_screen, controller()->current_screen()); - ASSERT_EQ(3, network_screen->GetItemCount()); - EXPECT_EQ(ASCIIToWide(wifi_.name()), network_screen->GetItemAt(1)); - EXPECT_EQ(ASCIIToWide(cellular_.name()), network_screen->GetItemAt(2)); - - // Ethernet, WiFi & Cellular - connected. - EthernetExpectations(true, false); - WifiExpectations(true, false); - CellularExpectations(true, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - CellularNameExpectation(cellular_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(network_screen, controller()->current_screen()); - ASSERT_EQ(4, network_screen->GetItemCount()); - EXPECT_EQ(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), - network_screen->GetItemAt(1)); - EXPECT_EQ(ASCIIToWide(wifi_.name()), network_screen->GetItemAt(2)); - EXPECT_EQ(ASCIIToWide(cellular_.name()), network_screen->GetItemAt(3)); - // Ethernet is only preselected once. - EXPECT_EQ(0, network_view->GetSelectedNetworkItem()); -} + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_network_library_, Connected()) + .WillOnce(Return(true)); -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, EthernetSelected) { - ASSERT_TRUE(controller()); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); - NetworkScreen* network_screen = controller()->GetNetworkScreen(); - ASSERT_TRUE(network_screen != NULL); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); - - // Emulate connecting to Ethernet. - EthernetExpectations(false, true); - WifiCellularNetworksExpectations(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), - network_screen->GetItemAt(1)); - ASSERT_EQ(network_screen, controller()->current_screen()); + network_screen->NetworkChanged(mock_network_library_); + EXPECT_TRUE(network_view->IsContinueEnabled()); - // Emulate combobox selection - nothing happens. - DummyComboboxModel combobox_model; - views::Combobox combobox(&combobox_model); - network_screen->ItemChanged(&combobox, 0, 1); - network_view->SetSelectedNetworkItem(1); - ui_test_utils::RunAllPendingInMessageLoop(); - ASSERT_EQ(network_screen, controller()->current_screen()); - - // Emulate connected Ethernet - it should be preselected. - EthernetExpectations(true, false); - WifiCellularNetworksExpectations(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(1, network_view->GetSelectedNetworkItem()); - ASSERT_EQ(network_screen, controller()->current_screen()); - - // "Continue" button with connected network should proceed to next screen. EmulateContinueButtonExit(network_screen); } -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, WifiSelected) { +IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Wifi) { ASSERT_TRUE(controller()); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); NetworkScreen* network_screen = controller()->GetNetworkScreen(); ASSERT_TRUE(network_screen != NULL); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); - - EthernetExpectations(false, false); - SetupWifiNetwork(false, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(std::string()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(ASCIIToWide(wifi_.name()), network_screen->GetItemAt(1)); - - DummyComboboxModel combobox_model; - views::Combobox combobox(&combobox_model); - - // Emulate combobox selection. - EthernetExpectations(false, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(std::string()); - network_screen->ItemChanged(&combobox, 0, 1); - network_view->SetSelectedNetworkItem(1); - EXPECT_CALL(*mock_network_library_, - ConnectToWifiNetwork(A<WifiNetwork>(), std::string(), - std::string(), std::string())) - .Times(1); - ui_test_utils::RunAllPendingInMessageLoop(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - - // Emulate connecting to WiFi network. - EthernetExpectations(false, false); - SetupWifiNetwork(false, true); - WifiExpectations(false, true); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); ASSERT_EQ(network_screen, controller()->current_screen()); - // Emulate connected WiFi network. - EthernetExpectations(false, false); - SetupWifiNetwork(true, false); - WifiExpectations(true, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ui_test_utils::RunAllPendingInMessageLoop(); - ASSERT_EQ(network_screen, controller()->current_screen()); + NetworkSelectionView* network_view = network_screen->view(); + ASSERT_TRUE(network_view != NULL); + EXPECT_FALSE(network_view->IsContinueEnabled()); + + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, cellular_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, ethernet_connecting()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connecting()) + .WillOnce((Return(true))); + std::string wifi_name = "wifi"; + EXPECT_CALL(*mock_network_library_, wifi_name()) + .WillOnce(ReturnRef(wifi_name)); + + network_screen->NetworkChanged(mock_network_library_); + EXPECT_FALSE(network_view->IsContinueEnabled()); + EXPECT_TRUE(network_view->IsConnecting()); + + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_network_library_, Connected()) + .WillOnce(Return(true)); + + network_screen->NetworkChanged(mock_network_library_); + EXPECT_TRUE(network_view->IsContinueEnabled()); - // "Continue" button with connected network should proceed to next screen. EmulateContinueButtonExit(network_screen); } -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, CellularSelected) { +IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Cellular) { ASSERT_TRUE(controller()); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); NetworkScreen* network_screen = controller()->GetNetworkScreen(); ASSERT_TRUE(network_screen != NULL); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); - - EthernetExpectations(false, false); - SetupCellularNetwork(false, false); - WifiCellularNetworksExpectations(); - CellularNameExpectation(std::string()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - EXPECT_EQ(ASCIIToWide(cellular_.name()), network_screen->GetItemAt(1)); - - DummyComboboxModel combobox_model; - views::Combobox combobox(&combobox_model); - - // Emulate combobox selection. - EthernetExpectations(false, false); - WifiCellularNetworksExpectations(); - CellularNameExpectation(std::string()); - network_screen->ItemChanged(&combobox, 0, 1); - network_view->SetSelectedNetworkItem(1); - EXPECT_CALL(*mock_network_library_, ConnectToCellularNetwork(_)) - .Times(1); - ui_test_utils::RunAllPendingInMessageLoop(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - - // Emulate connecting to cellular network. - EthernetExpectations(false, false); - SetupCellularNetwork(false, true); - CellularExpectations(false, true); - WifiCellularNetworksExpectations(); - CellularNameExpectation(cellular_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); ASSERT_EQ(network_screen, controller()->current_screen()); - // Emulate connected cellular network. - EthernetExpectations(false, false); - SetupCellularNetwork(true, false); - CellularExpectations(true, false); - WifiCellularNetworksExpectations(); - CellularNameExpectation(cellular_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ui_test_utils::RunAllPendingInMessageLoop(); - ASSERT_EQ(network_screen, controller()->current_screen()); + NetworkSelectionView* network_view = network_screen->view(); + ASSERT_TRUE(network_view != NULL); + EXPECT_FALSE(network_view->IsContinueEnabled()); + + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, cellular_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, ethernet_connecting()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connecting()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, cellular_connecting()) + .WillOnce((Return(true))); + std::string cellular_name = "3G"; + EXPECT_CALL(*mock_network_library_, cellular_name()) + .WillOnce(ReturnRef(cellular_name)); + + network_screen->NetworkChanged(mock_network_library_); + EXPECT_FALSE(network_view->IsContinueEnabled()); + EXPECT_TRUE(network_view->IsConnecting()); + + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_network_library_, Connected()) + .WillOnce(Return(true)); + + network_screen->NetworkChanged(mock_network_library_); + EXPECT_TRUE(network_view->IsContinueEnabled()); - // "Continue" button with connected network should proceed to next screen. EmulateContinueButtonExit(network_screen); } -IN_PROC_BROWSER_TEST_F(NetworkScreenTest, WifiWaiting) { +IN_PROC_BROWSER_TEST_F(NetworkScreenTest, Timeout) { ASSERT_TRUE(controller()); - NetworkLibrary* network_library = - chromeos::CrosLibrary::Get()->GetNetworkLibrary(); NetworkScreen* network_screen = controller()->GetNetworkScreen(); ASSERT_TRUE(network_screen != NULL); - NetworkSelectionView* network_view = network_screen->view(); - ASSERT_TRUE(network_view != NULL); - - EthernetExpectations(false, false); - SetupWifiNetwork(false, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(std::string()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - - DummyComboboxModel combobox_model; - views::Combobox combobox(&combobox_model); - - // Emulate combobox selection. - EthernetExpectations(false, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(std::string()); - network_screen->ItemChanged(&combobox, 0, 1); - network_view->SetSelectedNetworkItem(1); - EXPECT_CALL(*mock_network_library_, - ConnectToWifiNetwork(A<WifiNetwork>(), std::string(), - std::string(), std::string())) - .Times(1); - ui_test_utils::RunAllPendingInMessageLoop(); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ASSERT_EQ(2, network_screen->GetItemCount()); - - // Emulate connecting to WiFi network. - EthernetExpectations(false, false); - SetupWifiNetwork(false, true); - WifiExpectations(false, true); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - - // Continue but wait for connection. - scoped_ptr<MockScreenObserver> - mock_screen_observer(new MockScreenObserver()); - EXPECT_CALL(*mock_screen_observer, - OnExit(ScreenObserver::NETWORK_CONNECTED)) - .Times(1); - controller()->set_observer(mock_screen_observer.get()); - DummyButtonListener button_listener; - views::TextButton button(&button_listener, L"Button"); - views::MouseEvent event(views::Event::ET_MOUSE_RELEASED, - 0, 0, - views::Event::EF_LEFT_BUTTON_DOWN); - network_screen->ButtonPressed(&button, event); - ui_test_utils::RunAllPendingInMessageLoop(); ASSERT_EQ(network_screen, controller()->current_screen()); - // Emulate connected WiFi network. - EthernetExpectations(false, false); - SetupWifiNetwork(true, false); - WifiExpectations(true, false); - WifiCellularNetworksExpectations(); - WifiSsidExpectation(wifi_.name()); - NetworkChangedExpectations(true); - network_screen->NetworkChanged(network_library); - ui_test_utils::RunAllPendingInMessageLoop(); - controller()->set_observer(NULL); + NetworkSelectionView* network_view = network_screen->view(); + ASSERT_TRUE(network_view != NULL); + EXPECT_FALSE(network_view->IsContinueEnabled()); + + EXPECT_CALL(*mock_network_library_, ethernet_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, cellular_connected()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, ethernet_connecting()) + .WillOnce((Return(false))); + EXPECT_CALL(*mock_network_library_, wifi_connecting()) + .WillOnce((Return(true))); + std::string wifi_name = "wifi"; + EXPECT_CALL(*mock_network_library_, wifi_name()) + .WillOnce(ReturnRef(wifi_name)); + EXPECT_CALL(*mock_network_library_, Connected()) + .WillOnce(Return(false)); + + network_screen->NetworkChanged(mock_network_library_); + EXPECT_FALSE(network_view->IsContinueEnabled()); + EXPECT_TRUE(network_view->IsConnecting()); + + network_screen->OnConnectionTimeout(); + EXPECT_FALSE(network_view->IsContinueEnabled()); + EXPECT_FALSE(network_view->IsConnecting()); + + // Close infobubble with error message - it makes test stable. + network_screen->ClearErrors(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/network_screen_delegate.h b/chrome/browser/chromeos/login/network_screen_delegate.h index a0c911a..683c71b 100644 --- a/chrome/browser/chromeos/login/network_screen_delegate.h +++ b/chrome/browser/chromeos/login/network_screen_delegate.h @@ -4,24 +4,32 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SCREEN_DELEGATE_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SCREEN_DELEGATE_H_ +#pragma once #include "app/combobox_model.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "views/controls/button/button.h" #include "views/controls/combobox/combobox.h" +namespace gfx { +class Size; +} // namespace gfx + namespace chromeos { class LanguageSwitchMenu; // Interface that NetworkScreen exposes to the NetworkSelectionView. -class NetworkScreenDelegate : public ComboboxModel, - public views::Combobox::Listener, - public views::ButtonListener, +class NetworkScreenDelegate : public views::ButtonListener, public NetworkLibrary::Observer { public: + // Cleares all error notifications. + virtual void ClearErrors() = 0; + virtual LanguageSwitchMenu* language_switch_menu() = 0; + virtual gfx::Size size() const = 0; + protected: virtual ~NetworkScreenDelegate() {} }; diff --git a/chrome/browser/chromeos/login/network_selection_view.cc b/chrome/browser/chromeos/login/network_selection_view.cc index 2e206a1..4b80b9c 100644 --- a/chrome/browser/chromeos/login/network_selection_view.cc +++ b/chrome/browser/chromeos/login/network_selection_view.cc @@ -11,16 +11,21 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/language_switch_menu.h" #include "chrome/browser/chromeos/login/network_screen_delegate.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" -#include "chrome/browser/chromeos/login/language_switch_menu.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" +#include "chrome/browser/chromeos/status/network_dropdown_button.h" +#include "gfx/size.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/controls/button/native_button.h" -#include "views/controls/combobox/combobox.h" #include "views/controls/label.h" #include "views/controls/throbber.h" +#include "views/grid_layout.h" +#include "views/standard_layout.h" #include "views/widget/widget.h" #include "views/widget/widget_gtk.h" #include "views/window/non_client_view.h" @@ -28,64 +33,242 @@ #include "views/window/window_gtk.h" using views::Background; +using views::GridLayout; using views::Label; -using views::SmoothedThrobber; using views::View; using views::Widget; using views::WidgetGtk; namespace { -const int kWelcomeLabelY = 170; -const int kContinueButtonSpacingX = 30; -const int kSpacing = 25; -const int kHorizontalSpacing = 25; -const int kSelectionBoxWidthMin = 200; +enum kLayoutColumnsets { + STANDARD_ROW, + THROBBER_ROW, +}; + +enum kContentsLayoutColumnsets { + WELCOME_ROW, + CONTENTS_ROW, +}; + +// Grid layout constants. +const int kBorderSize = 10; +const int kWelcomeTitlePadding = 10; +const int kPaddingColumnWidth = 55; +const int kMediumPaddingColumnWidth = 20; +const int kControlPaddingRow = 15; + +// Fixed size for language/network controls height. const int kSelectionBoxHeight = 29; -const int kSelectionBoxSpacing = 7; // Menu button is drawn using our custom icons in resources. See // TextButtonBorder::Paint() for details. So this offset compensate // horizontal size, eaten by those icons. -const int kMenuButtonHorizontalOffset = 1; +const int kMenuHorizontalOffset = -1; // Vertical addition to the menu window to make it appear exactly below // MenuButton. -const int kMenuButtonVerticalOffset = 3; +const int kMenuVerticalOffset = 3; -const SkColor kWelcomeColor = 0xFF1D6AB1; +// Offset that compensates menu width so that it matches +// menu button visual width when being in pushed state. +const int kMenuWidthOffset = 6; -const int kThrobberFrameMs = 60; -const int kThrobberStartDelayMs = 500; +const SkColor kWelcomeColor = 0xFFCDD3D6; } // namespace namespace chromeos { +// NetworkDropdownButton with custom accelerator enabled. +class NetworkControlWithAccelerators : public NetworkDropdownButton { + public: + NetworkControlWithAccelerators(bool browser_mode, + gfx::NativeWindow parent_window, + NetworkScreenDelegate* delegate) + : NetworkDropdownButton(browser_mode, parent_window), + delegate_(delegate), + accel_clear_errors_(views::Accelerator(app::VKEY_ESCAPE, + false, false, false)) { + AddAccelerator(accel_clear_errors_); + } + + // Overridden from View: + bool AcceleratorPressed(const views::Accelerator& accel) { + if (accel == accel_clear_errors_) { + delegate_->ClearErrors(); + return true; + } + return false; + } + + // Overridden from MenuButton: + virtual bool Activate() { + delegate_->ClearErrors(); + return MenuButton::Activate(); + } + + private: + NetworkScreenDelegate* delegate_; + + // ESC accelerator for closing error info bubble. + views::Accelerator accel_clear_errors_; + + DISALLOW_COPY_AND_ASSIGN(NetworkControlWithAccelerators); +}; + +// MenuButton with custom processing on focus events. +class NotifyingMenuButton : public views::MenuButton { + public: + NotifyingMenuButton(views::ButtonListener* listener, + const std::wstring& text, + views::ViewMenuDelegate* menu_delegate, + bool show_menu_marker, + NetworkScreenDelegate* delegate) + : MenuButton(listener, text, menu_delegate, show_menu_marker), + delegate_(delegate) {} + + // Overridden from View: + virtual void DidGainFocus() { + delegate_->ClearErrors(); + } + + private: + NetworkScreenDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(NotifyingMenuButton); +}; + NetworkSelectionView::NetworkSelectionView(NetworkScreenDelegate* delegate) - : network_combobox_(NULL), + : contents_view_(NULL), languages_menubutton_(NULL), welcome_label_(NULL), select_language_label_(NULL), select_network_label_(NULL), connecting_network_label_(NULL), + network_dropdown_(NULL), continue_button_(NULL), - throbber_(NULL), + throbber_(CreateDefaultSmoothedThrobber()), + proxy_settings_link_(NULL), delegate_(delegate) { } NetworkSelectionView::~NetworkSelectionView() { - network_combobox_->set_listener(NULL); - network_combobox_ = NULL; throbber_->Stop(); throbber_ = NULL; } +void NetworkSelectionView::AddControlsToLayout(const gfx::Size& size, + views::GridLayout* contents_layout) { + if (IsConnecting()) { + const int v_padding = (size.height() - + throbber_->GetPreferredSize().height()) / 2; + contents_layout->AddPaddingRow(0, v_padding); + contents_layout->StartRow(0, THROBBER_ROW); + contents_layout->AddView(connecting_network_label_); + contents_layout->AddView(throbber_); + contents_layout->AddPaddingRow(0, v_padding); + } else { + const int v_padding = (size.height() - + 3 * kControlPaddingRow - 2 * kSelectionBoxHeight - + proxy_settings_link_->GetPreferredSize().height() - + continue_button_->GetPreferredSize().height()) / 2; + contents_layout->AddPaddingRow(0, v_padding); + contents_layout->StartRow(0, STANDARD_ROW); + contents_layout->AddView(select_language_label_); + contents_layout->AddView(languages_menubutton_, 1, 1, + GridLayout::FILL, GridLayout::FILL, + languages_menubutton_->GetPreferredSize().width(), + kSelectionBoxHeight); + contents_layout->AddPaddingRow(0, kControlPaddingRow); + contents_layout->StartRow(0, STANDARD_ROW); + contents_layout->AddView(select_network_label_); + contents_layout->AddView(network_dropdown_, 1, 1, + GridLayout::FILL, GridLayout::FILL, + network_dropdown_->GetPreferredSize().width(), + kSelectionBoxHeight); + contents_layout->AddPaddingRow(0, kControlPaddingRow); + contents_layout->StartRow(0, STANDARD_ROW); + contents_layout->SkipColumns(1); + contents_layout->AddView(proxy_settings_link_, 1, 1, + GridLayout::LEADING, GridLayout::CENTER); + contents_layout->AddPaddingRow(0, kControlPaddingRow); + contents_layout->StartRow(0, STANDARD_ROW); + contents_layout->SkipColumns(1); + contents_layout->AddView(continue_button_, 1, 1, + GridLayout::LEADING, GridLayout::CENTER); + contents_layout->AddPaddingRow(0, v_padding); + } +} + +void NetworkSelectionView::InitLayout() { + gfx::Size screen_size = delegate_->size(); + const int widest_label = std::max( + select_language_label_->GetPreferredSize().width(), + select_network_label_->GetPreferredSize().width()); + const int dropdown_width = screen_size.width() - 2 * kBorderSize - + 2 * kPaddingColumnWidth - kMediumPaddingColumnWidth - widest_label; + delegate_->language_switch_menu()->SetFirstLevelMenuWidth( + dropdown_width - kMenuWidthOffset); + + // Define layout and column set for entire screen (welcome + screen). + views::GridLayout* screen_layout = new views::GridLayout(this); + SetLayoutManager(screen_layout); + views::ColumnSet* column_set = screen_layout->AddColumnSet(WELCOME_ROW); + const int welcome_width = screen_size.width() - 2 * kWelcomeTitlePadding - + 2 * kBorderSize; + column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::FIXED, welcome_width, welcome_width); + column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize); + column_set = screen_layout->AddColumnSet(CONTENTS_ROW); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::FIXED, screen_size.width(), screen_size.width()); + screen_layout->StartRow(0, WELCOME_ROW); + screen_layout->AddView(welcome_label_); + screen_layout->StartRow(0, CONTENTS_ROW); + screen_layout->AddView(contents_view_); + + // Welcome label size might have been changed after adding to grid layout. + // Screen size includes welcome label height & border on each side. + screen_size.set_height(screen_size.height() - 2 * kBorderSize - + welcome_label_->GetPreferredSize().height()); + + // Define layout and column set for screen contents. + views::GridLayout* contents_layout = new views::GridLayout(contents_view_); + contents_view_->SetLayoutManager(contents_layout); + + column_set = contents_layout->AddColumnSet(STANDARD_ROW); + column_set->AddPaddingColumn(0, kPaddingColumnWidth); + column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, + GridLayout::FIXED, widest_label, widest_label); + column_set->AddPaddingColumn(0, kMediumPaddingColumnWidth); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, + GridLayout::FIXED, dropdown_width, dropdown_width); + column_set->AddPaddingColumn(1, kPaddingColumnWidth); + + const int h_padding = (screen_size.width() - 2 * kBorderSize - + connecting_network_label_->GetPreferredSize().width() - + throbber_->GetPreferredSize().width()) / 2; + column_set = contents_layout->AddColumnSet(THROBBER_ROW); + column_set->AddPaddingColumn(0, h_padding); + column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1, + GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, h_padding); + + AddControlsToLayout(screen_size, contents_layout); +} + void NetworkSelectionView::Init() { + contents_view_ = new views::View(); + // Use rounded rect background. views::Painter* painter = CreateWizardPainter( &BorderDefinition::kScreenBorder); - set_background( + contents_view_->set_background( views::Background::CreateBackgroundPainter(true, painter)); ResourceBundle& rb = ResourceBundle::GetSharedInstance(); @@ -100,41 +283,36 @@ void NetworkSelectionView::Init() { select_language_label_ = new views::Label(); select_language_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); + languages_menubutton_ = new NotifyingMenuButton( + NULL, std::wstring(), delegate_->language_switch_menu(), true, delegate_); + languages_menubutton_->SetFocusable(true); + languages_menubutton_->SetNormalHasBorder(true); + // Menu is positioned by bottom right corner of the MenuButton. + delegate_->language_switch_menu()->set_menu_offset(kMenuHorizontalOffset, + kMenuVerticalOffset); + select_network_label_ = new views::Label(); select_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); + network_dropdown_ = new NetworkControlWithAccelerators(false, + GetNativeWindow(), + delegate_); + network_dropdown_->SetNormalHasBorder(true); + network_dropdown_->SetFocusable(true); + connecting_network_label_ = new views::Label(); connecting_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); connecting_network_label_->SetVisible(false); - throbber_ = new views::SmoothedThrobber(kThrobberFrameMs); - throbber_->SetFrames( - ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_SPINNER)); - throbber_->set_start_delay_ms(kThrobberStartDelayMs); - AddChildView(throbber_); - - network_combobox_ = new views::Combobox(delegate_); - network_combobox_->set_listener(delegate_); - - languages_menubutton_ = new views::MenuButton( - NULL, std::wstring(), delegate_->language_switch_menu(), true); - languages_menubutton_->SetFocusable(true); - languages_menubutton_->SetNormalHasBorder(true); - delegate_->language_switch_menu()->set_menu_offset( - kMenuButtonHorizontalOffset, kMenuButtonVerticalOffset); - - AddChildView(welcome_label_); - AddChildView(select_language_label_); - AddChildView(select_network_label_); - AddChildView(connecting_network_label_); - AddChildView(languages_menubutton_); - AddChildView(network_combobox_); + proxy_settings_link_ = new views::Link(); + proxy_settings_link_->SetController(this); + proxy_settings_link_->SetVisible(true); + proxy_settings_link_->SetFocusable(true); UpdateLocalizedStrings(); } void NetworkSelectionView::UpdateLocalizedStrings() { - RecreateNativeControls(); languages_menubutton_->SetText( delegate_->language_switch_menu()->GetCurrentLocaleName()); welcome_label_->SetText(l10n_util::GetStringF(IDS_NETWORK_SELECTION_TITLE, @@ -143,130 +321,42 @@ void NetworkSelectionView::UpdateLocalizedStrings() { l10n_util::GetString(IDS_LANGUAGE_SELECTION_SELECT)); select_network_label_->SetText( l10n_util::GetString(IDS_NETWORK_SELECTION_SELECT)); - continue_button_->SetLabel( - l10n_util::GetString(IDS_NETWORK_SELECTION_CONTINUE_BUTTON)); + proxy_settings_link_->SetText( + l10n_util::GetString(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON)); + RecreateNativeControls(); UpdateConnectingNetworkLabel(); + network_dropdown_->Refresh(); + InitLayout(); } //////////////////////////////////////////////////////////////////////////////// // views::View: implementation: -void NetworkSelectionView::ChildPreferredSizeChanged(View* child) { - Layout(); - SchedulePaint(); -} - -void NetworkSelectionView::LocaleChanged() { +void NetworkSelectionView::OnLocaleChanged() { UpdateLocalizedStrings(); - NetworkModelChanged(); - // Explicitly set selected item - index 0 is a localized string. - if (GetSelectedNetworkItem() <= 0 && - delegate_->GetItemCount() > 0) { - SetSelectedNetworkItem(0); - } + // Proxy settings dialog contains localized title. Zap it. + proxy_settings_dialog_.reset(NULL); + Layout(); SchedulePaint(); } -gfx::Size NetworkSelectionView::GetPreferredSize() { - return gfx::Size(width(), height()); -} - -void NetworkSelectionView::Layout() { - gfx::Insets insets = GetInsets(); - int max_width = this->width() - insets.width() - 2 * kHorizontalSpacing; - welcome_label_->SizeToFit(max_width); - int y = kWelcomeLabelY; - y -= welcome_label_->GetPreferredSize().height() / 2; - - welcome_label_->SetBounds( - (width() - welcome_label_->GetPreferredSize().width()) / 2, - y, - welcome_label_->GetPreferredSize().width(), - welcome_label_->GetPreferredSize().height()); - y += welcome_label_->GetPreferredSize().height() + kSpacing; - - // Use menu preffered size to calculate boxes width accordingly. - int box_width = delegate_->language_switch_menu()->GetFirstLevelMenuWidth() + - kMenuButtonHorizontalOffset * 2; - const int widest_label = std::max( - select_language_label_->GetPreferredSize().width(), - select_network_label_->GetPreferredSize().width()); - if (box_width < kSelectionBoxWidthMin) { - box_width = kSelectionBoxWidthMin; - delegate_->language_switch_menu()->SetFirstLevelMenuWidth( - box_width - kMenuButtonHorizontalOffset * 2); - } else if (widest_label + box_width + 2 * kHorizontalSpacing > width()) { - box_width = width() - widest_label - 2 * kHorizontalSpacing; - } - const int labels_x = (width() - widest_label - box_width) / 2; - select_language_label_->SetBounds( - labels_x, - y, - select_language_label_->GetPreferredSize().width(), - select_language_label_->GetPreferredSize().height()); - - const int selection_box_x = labels_x + widest_label + kHorizontalSpacing; - const int label_y_offset = - (kSelectionBoxHeight - - select_language_label_->GetPreferredSize().height()) / 2; - languages_menubutton_->SetBounds(selection_box_x, y - label_y_offset, - box_width, kSelectionBoxHeight); - - y += kSelectionBoxHeight + kSelectionBoxSpacing; - select_network_label_->SetBounds( - labels_x, - y, - select_network_label_->GetPreferredSize().width(), - select_network_label_->GetPreferredSize().height()); - - connecting_network_label_->SetBounds( - kHorizontalSpacing, - y, - width() - kHorizontalSpacing * 2, - connecting_network_label_->GetPreferredSize().height()); - - throbber_->SetBounds( - width() / 2 + connecting_network_label_->GetPreferredSize().width() / 2 + - kHorizontalSpacing, - y + (connecting_network_label_->GetPreferredSize().height() - - throbber_->GetPreferredSize().height()) / 2, - throbber_->GetPreferredSize().width(), - throbber_->GetPreferredSize().height()); - - network_combobox_->SetBounds(selection_box_x, y - label_y_offset, - box_width, kSelectionBoxHeight); - - y = height() - continue_button_->GetPreferredSize().height() - kSpacing; - continue_button_->SetBounds( - width() - kContinueButtonSpacingX - - continue_button_->GetPreferredSize().width(), - y, - continue_button_->GetPreferredSize().width(), - continue_button_->GetPreferredSize().height()); - - // Need to refresh combobox layout explicitly. - network_combobox_->Layout(); - continue_button_->Layout(); +void NetworkSelectionView::ViewHierarchyChanged(bool is_add, + View* parent, + View* child) { + if (is_add && this == child) + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); } //////////////////////////////////////////////////////////////////////////////// // NetworkSelectionView, public: -int NetworkSelectionView::GetSelectedNetworkItem() const { - return network_combobox_->selected_item(); -} - -void NetworkSelectionView::SetSelectedNetworkItem(int index) { - network_combobox_->SetSelectedItem(index); -} - -gfx::NativeWindow NetworkSelectionView::GetNativeWindow() { +gfx::NativeWindow NetworkSelectionView::GetNativeWindow() const { return GTK_WINDOW(static_cast<WidgetGtk*>(GetWidget())->GetNativeView()); } -void NetworkSelectionView::NetworkModelChanged() { - network_combobox_->ModelChanged(); +views::View* NetworkSelectionView::GetNetworkControlView() const { + return network_dropdown_; } void NetworkSelectionView::ShowConnectingStatus(bool connecting, @@ -276,32 +366,62 @@ void NetworkSelectionView::ShowConnectingStatus(bool connecting, select_language_label_->SetVisible(!connecting); languages_menubutton_->SetVisible(!connecting); select_network_label_->SetVisible(!connecting); - network_combobox_->SetVisible(!connecting); + network_dropdown_->SetVisible(!connecting); continue_button_->SetVisible(!connecting); + proxy_settings_link_->SetVisible(!connecting); connecting_network_label_->SetVisible(connecting); + InitLayout(); Layout(); if (connecting) { throbber_->Start(); + network_dropdown_->CancelMenu(); } else { throbber_->Stop(); } } +bool NetworkSelectionView::IsConnecting() const { + return connecting_network_label_->IsVisible(); +} + void NetworkSelectionView::EnableContinue(bool enabled) { if (continue_button_) continue_button_->SetEnabled(enabled); } +bool NetworkSelectionView::IsContinueEnabled() const { + return continue_button_ && continue_button_->IsEnabled(); +} + +//////////////////////////////////////////////////////////////////////////////// +// views::LinkController implementation: + +void NetworkSelectionView::LinkActivated(views::Link* source, int) { + if (source == proxy_settings_link_) { + if (!proxy_settings_dialog_.get()) { + static const char kProxySettingsURL[] = "chrome://settings/proxy"; + proxy_settings_dialog_.reset(new LoginHtmlDialog( + this, + GetNativeWindow(), + l10n_util::GetString(IDS_OPTIONS_PROXY_TAB_LABEL), + GURL(kProxySettingsURL))); + } + proxy_settings_dialog_->Show(); + } +} + //////////////////////////////////////////////////////////////////////////////// // NetworkSelectionView, private: void NetworkSelectionView::RecreateNativeControls() { // There is no way to get native button preferred size after the button was // sized so delete and recreate the button on text update. + bool is_continue_enabled = IsContinueEnabled(); delete continue_button_; - continue_button_ = new views::NativeButton(delegate_, std::wstring()); - continue_button_->SetEnabled(false); - AddChildView(continue_button_); + continue_button_ = new views::NativeButton( + delegate_, + l10n_util::GetString(IDS_NETWORK_SELECTION_CONTINUE_BUTTON)); + continue_button_->SetEnabled(is_continue_enabled); } void NetworkSelectionView::UpdateConnectingNetworkLabel() { diff --git a/chrome/browser/chromeos/login/network_selection_view.h b/chrome/browser/chromeos/login/network_selection_view.h index 5843e33..b089ff8 100644 --- a/chrome/browser/chromeos/login/network_selection_view.h +++ b/chrome/browser/chromeos/login/network_selection_view.h @@ -4,28 +4,39 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SELECTION_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_NETWORK_SELECTION_VIEW_H_ +#pragma once -#include <string> - +#include "base/scoped_ptr.h" +#include "base/string16.h" +#include "chrome/browser/chromeos/login/login_html_dialog.h" #include "views/controls/button/menu_button.h" +#include "views/controls/link.h" #include "views/view.h" #include "views/widget/widget_gtk.h" #include "views/window/window_delegate.h" +namespace gfx { +class Size; +} // namespace gfx + namespace views { class Combobox; +class GridLayout; class Label; class NativeButton; -class SmoothedThrobber; +class Throbber; } // namespace views namespace chromeos { +class NetworkDropdownButton; class NetworkScreenDelegate; class ScreenObserver; // View for the network selection/initial welcome screen. -class NetworkSelectionView : public views::View { +class NetworkSelectionView : public views::View, + public views::LinkController, + public LoginHtmlDialog::Delegate { public: explicit NetworkSelectionView(NetworkScreenDelegate* delegate); virtual ~NetworkSelectionView(); @@ -36,31 +47,47 @@ class NetworkSelectionView : public views::View { // Update strings from the resources. Executed on language change. void UpdateLocalizedStrings(); - // views::View: implementation: - virtual gfx::Size GetPreferredSize(); - virtual void Layout(); - - // Gets/Sets the selected item in the network combobox. - int GetSelectedNetworkItem() const; - void SetSelectedNetworkItem(int index); + // Returns top level native window for the view. + gfx::NativeWindow GetNativeWindow() const; - gfx::NativeWindow GetNativeWindow(); - - // Inform the network combobox that its model changed. - void NetworkModelChanged(); + // Returns network control view. + views::View* GetNetworkControlView() const; // Shows network connecting status or network selection otherwise. void ShowConnectingStatus(bool connecting, const string16& network_id); + // Returns true if only throbber is visible, the view is in waiting status. + bool IsConnecting() const; + // Sets whether continue control is enabled. void EnableContinue(bool enabled); + // Returns whether continue button is enabled. + bool IsContinueEnabled() const; + + // views::LinkController implementation. + virtual void LinkActivated(views::Link* source, int); + protected: // Overridden from views::View. - virtual void ChildPreferredSizeChanged(View* child); - virtual void LocaleChanged(); + virtual void OnLocaleChanged(); + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + + // LoginHtmlDialog::Delegate implementation: + virtual void OnDialogClosed() {} private: + // Add screen controls to the contents layout specified. + // Based on state (connecting to the network or not) + // different controls are added. + void AddControlsToLayout(const gfx::Size& size, + views::GridLayout* contents_layout); + + // Initializes grid layout of the screen. Called on language change too. + void InitLayout(); + // Delete and recreate native controls that // fail to update preferred size after string update. void RecreateNativeControls(); @@ -68,15 +95,19 @@ class NetworkSelectionView : public views::View { // Updates text on label with currently connecting network. void UpdateConnectingNetworkLabel(); - // Dialog controls. - views::Combobox* network_combobox_; + // View that contains defines screen contents. + views::View* contents_view_; + + // Screen controls. views::MenuButton* languages_menubutton_; views::Label* welcome_label_; views::Label* select_language_label_; views::Label* select_network_label_; views::Label* connecting_network_label_; + NetworkDropdownButton* network_dropdown_; views::NativeButton* continue_button_; - views::SmoothedThrobber* throbber_; + views::Throbber* throbber_; + views::Link* proxy_settings_link_; // NetworkScreen delegate. NetworkScreenDelegate* delegate_; @@ -84,6 +115,9 @@ class NetworkSelectionView : public views::View { // Id of the network that is in process of connecting. string16 network_id_; + // Dialog used for to launch proxy settings. + scoped_ptr<LoginHtmlDialog> proxy_settings_dialog_; + DISALLOW_COPY_AND_ASSIGN(NetworkSelectionView); }; diff --git a/chrome/browser/chromeos/login/new_user_view.cc b/chrome/browser/chromeos/login/new_user_view.cc index 397821d..ae97457 100644 --- a/chrome/browser/chromeos/login/new_user_view.cc +++ b/chrome/browser/chromeos/login/new_user_view.cc @@ -10,20 +10,22 @@ #include <algorithm> #include <vector> +#include "app/keyboard_codes.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/callback.h" #include "base/command_line.h" -#include "base/keyboard_codes.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/process_util.h" -#include "base/utf_string_conversions.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" -#include "chrome/browser/google_util.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" +#include "grit/app_resources.h" +#include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "views/controls/button/native_button.h" #include "views/controls/label.h" @@ -37,20 +39,15 @@ using views::WidgetGtk; namespace { -// NOTE: When adding new controls check RecreateNativeControls() -// that |sign_in_button_| is added with correct index. -const int kSignInButtonFocusOrderIndex = 3; const int kTextfieldWidth = 286; +const int kSplitterHeight = 1; const int kRowPad = 7; const int kColumnPad = 7; -const int kLanguagesMenuWidth = 200; const int kLanguagesMenuHeight = 30; +const SkColor kLanguagesMenuTextColor = 0xFF999999; const SkColor kErrorColor = 0xFF8F384F; const char kDefaultDomain[] = "@gmail.com"; -const char kAccountRecoveryHelpUrl[] = - "http://www.google.com/support/accounts/bin/answer.py?answer=48598"; - // Textfield that adds domain to the entered username if focus is lost and // username doesn't have full domain. class UsernameField : public views::Textfield { @@ -76,23 +73,33 @@ class UsernameField : public views::Textfield { namespace chromeos { -NewUserView::NewUserView(Delegate* delegate, bool need_border) +NewUserView::NewUserView(Delegate* delegate, + bool need_border, + bool need_browse_without_signin) : username_field_(NULL), password_field_(NULL), title_label_(NULL), + title_hint_label_(NULL), + splitter_(NULL), sign_in_button_(NULL), create_account_link_(NULL), - cant_access_account_link_(NULL), browse_without_signin_link_(NULL), languages_menubutton_(NULL), throbber_(NULL), - accel_focus_user_(views::Accelerator(base::VKEY_U, false, false, true)), - accel_focus_pass_(views::Accelerator(base::VKEY_P, false, false, true)), + accel_focus_pass_(views::Accelerator(app::VKEY_P, false, false, true)), + accel_focus_user_(views::Accelerator(app::VKEY_U, false, false, true)), + accel_login_off_the_record_( + views::Accelerator(app::VKEY_B, false, false, true)), + accel_enable_accessibility_(WizardAccessibilityHelper::GetAccelerator()), delegate_(delegate), ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)), focus_delayed_(false), login_in_process_(false), - need_border_(need_border) { + need_border_(need_border), + need_browse_without_signin_(need_browse_without_signin), + need_create_account_(false), + languages_menubutton_order_(-1), + sign_in_button_order_(-1) { } NewUserView::~NewUserView() { @@ -105,14 +112,12 @@ void NewUserView::Init() { views::Painter* painter = CreateWizardPainter( &BorderDefinition::kScreenBorder); set_background(views::Background::CreateBackgroundPainter(true, painter)); - } else { - set_background(views::Background::CreateSolidBackground( - BorderDefinition::kScreenBorder.top_color)); } // Set up fonts. ResourceBundle& rb = ResourceBundle::GetSharedInstance(); gfx::Font title_font = rb.GetFont(ResourceBundle::MediumBoldFont); + gfx::Font title_hint_font = rb.GetFont(ResourceBundle::BoldFont); title_label_ = new views::Label(); title_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); @@ -120,6 +125,18 @@ void NewUserView::Init() { title_label_->SetMultiLine(true); AddChildView(title_label_); + title_hint_label_ = new views::Label(); + title_hint_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + title_hint_label_->SetFont(title_hint_font); + title_hint_label_->SetColor(SK_ColorGRAY); + title_hint_label_->SetMultiLine(true); + AddChildView(title_hint_label_); + + splitter_ = new views::View(); + splitter_->set_background( + views::Background::CreateSolidBackground(SK_ColorGRAY)); + AddChildView(splitter_); + username_field_ = new UsernameField(); AddChildView(username_field_); @@ -129,18 +146,24 @@ void NewUserView::Init() { throbber_ = CreateDefaultSmoothedThrobber(); AddChildView(throbber_); - InitLink(&create_account_link_); - InitLink(&cant_access_account_link_); - InitLink(&browse_without_signin_link_); - language_switch_menu_.InitLanguageMenu(); - languages_menubutton_ = new views::MenuButton( - NULL, std::wstring(), &language_switch_menu_, true); - languages_menubutton_->SetFocusable(true); + + RecreatePeculiarControls(); + + AddChildView(sign_in_button_); + if (need_create_account_) { + InitLink(&create_account_link_); + } + if (need_browse_without_signin_) { + InitLink(&browse_without_signin_link_); + } AddChildView(languages_menubutton_); + // Set up accelerators. AddAccelerator(accel_focus_user_); AddAccelerator(accel_focus_pass_); + AddAccelerator(accel_login_off_the_record_); + AddAccelerator(accel_enable_accessibility_); UpdateLocalizedStrings(); RequestFocus(); @@ -151,55 +174,100 @@ void NewUserView::Init() { if (!CrosLibrary::Get()->EnsureLoaded()) { EnableInputControls(false); } + + // The 'Sign in' button should be disabled when there is no text in the + // username and password fields. + sign_in_button_->SetEnabled(false); } bool NewUserView::AcceleratorPressed(const views::Accelerator& accelerator) { if (accelerator == accel_focus_user_) { username_field_->RequestFocus(); - return true; - } - - if (accelerator == accel_focus_pass_) { + } else if (accelerator == accel_focus_pass_) { password_field_->RequestFocus(); - return true; + } else if (accelerator == accel_login_off_the_record_) { + delegate_->OnLoginOffTheRecord(); + } else if (accelerator == accel_enable_accessibility_) { + WizardAccessibilityHelper::GetInstance()->EnableAccessibility(this); + } else { + return false; } - - return false; + return true; } -void NewUserView::RecreateNativeControls() { +void NewUserView::RecreatePeculiarControls() { + // PreferredSize reported by MenuButton (and TextField) is not able + // to shrink, only grow; so recreate on text change. + delete languages_menubutton_; + languages_menubutton_ = new views::MenuButton( + NULL, std::wstring(), &language_switch_menu_, true); + languages_menubutton_->set_menu_marker( + ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_MENU_DROPARROW_SHARP)); + languages_menubutton_->SetEnabledColor(kLanguagesMenuTextColor); + languages_menubutton_->SetFocusable(true); + // There is no way to get native button preferred size after the button was // sized so delete and recreate the button on text update. delete sign_in_button_; sign_in_button_ = new views::NativeButton(this, std::wstring()); - // Add button after label, user & password fields. - DCHECK(GetChildViewCount() >= kSignInButtonFocusOrderIndex); - AddChildView(kSignInButtonFocusOrderIndex, sign_in_button_); + UpdateSignInButtonState(); + if (!CrosLibrary::Get()->EnsureLoaded()) sign_in_button_->SetEnabled(false); } -void NewUserView::UpdateLocalizedStrings() { - RecreateNativeControls(); +void NewUserView::UpdateSignInButtonState() { + bool enabled = !username_field_->text().empty() && + !password_field_->text().empty(); + sign_in_button_->SetEnabled(enabled); +} - title_label_->SetText(l10n_util::GetString(IDS_LOGIN_TITLE)); +void NewUserView::AddChildView(View* view) { + // languages_menubutton_ and sign_in_button_ are recreated on text change, + // so we restore their original position in layout. + if (view == languages_menubutton_) { + if (languages_menubutton_order_ < 0) { + languages_menubutton_order_ = GetChildViewCount(); + } + views::View::AddChildView(languages_menubutton_order_, view); + } else if (view == sign_in_button_) { + if (sign_in_button_order_ < 0) { + sign_in_button_order_ = GetChildViewCount(); + } + views::View::AddChildView(sign_in_button_order_, view); + } else { + views::View::AddChildView(view); + } +} + +void NewUserView::UpdateLocalizedStrings() { + title_label_->SetText(l10n_util::GetStringF( + IDS_LOGIN_TITLE, l10n_util::GetString(IDS_PRODUCT_OS_NAME))); + title_hint_label_->SetText(l10n_util::GetString(IDS_LOGIN_TITLE_HINT)); username_field_->set_text_to_display_when_empty( l10n_util::GetStringUTF16(IDS_LOGIN_USERNAME)); password_field_->set_text_to_display_when_empty( l10n_util::GetStringUTF16(IDS_LOGIN_PASSWORD)); sign_in_button_->SetLabel(l10n_util::GetString(IDS_LOGIN_BUTTON)); - create_account_link_->SetText( - l10n_util::GetString(IDS_CREATE_ACCOUNT_BUTTON)); - cant_access_account_link_->SetText( - l10n_util::GetString(IDS_CANT_ACCESS_ACCOUNT_BUTTON)); - browse_without_signin_link_->SetText( - l10n_util::GetString(IDS_BROWSE_WITHOUT_SIGNING_IN_BUTTON)); + if (need_create_account_) { + create_account_link_->SetText( + l10n_util::GetString(IDS_CREATE_ACCOUNT_BUTTON)); + } + if (need_browse_without_signin_) { + browse_without_signin_link_->SetText( + l10n_util::GetString(IDS_BROWSE_WITHOUT_SIGNING_IN_BUTTON)); + } delegate_->ClearErrors(); languages_menubutton_->SetText(language_switch_menu_.GetCurrentLocaleName()); } -void NewUserView::LocaleChanged() { +void NewUserView::OnLocaleChanged() { + RecreatePeculiarControls(); UpdateLocalizedStrings(); + AddChildView(sign_in_button_); + AddChildView(languages_menubutton_); + Layout(); SchedulePaint(); } @@ -217,6 +285,7 @@ void NewUserView::ViewHierarchyChanged(bool is_add, MessageLoop::current()->PostTask(FROM_HERE, focus_grabber_factory_.NewRunnableMethod( &NewUserView::FocusFirstField)); + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); } } @@ -265,32 +334,49 @@ void NewUserView::Layout() { // Place language selection in top right corner. int x = std::max(0, - this->width() - insets.right() - kLanguagesMenuWidth - kColumnPad); + this->width() - insets.right() - + languages_menubutton_->GetPreferredSize().width() - kColumnPad); int y = insets.top() + kRowPad; int width = std::min(this->width() - insets.width() - 2 * kColumnPad, - kLanguagesMenuWidth); + languages_menubutton_->GetPreferredSize().width()); int height = kLanguagesMenuHeight; languages_menubutton_->SetBounds(x, y, width, height); + y += height + kRowPad; width = std::min(this->width() - insets.width() - 2 * kColumnPad, kTextfieldWidth); x = (this->width() - width) / 2; int max_width = this->width() - x - insets.right(); title_label_->SizeToFit(max_width); + title_hint_label_->SizeToFit(max_width); + + // Top align title and title hint. + y += setViewBounds(title_label_, x, y, max_width, false); + y += setViewBounds(title_hint_label_, x, y, max_width, false); + int title_end = y; + + // Center align all other controls. + int create_account_link_height = need_create_account_ ? + create_account_link_->GetPreferredSize().height() : 0; + int browse_without_signin_link_height = need_browse_without_signin_ ? + browse_without_signin_link_->GetPreferredSize().height() : 0; - height = title_label_->GetPreferredSize().height() + - username_field_->GetPreferredSize().height() + + height = username_field_->GetPreferredSize().height() + password_field_->GetPreferredSize().height() + sign_in_button_->GetPreferredSize().height() + - create_account_link_->GetPreferredSize().height() + - cant_access_account_link_->GetPreferredSize().height() + - browse_without_signin_link_->GetPreferredSize().height() + - 4 * kRowPad; - y = (this->height() - height) / 2; + create_account_link_height + + browse_without_signin_link_height + + 5 * kRowPad; + y += (this->height() - y - height) / 2; + + int corner_radius = need_border_ ? login::kScreenCornerRadius : 0; + splitter_->SetBounds(insets.left() - corner_radius / 2, + title_end + (y - title_end) / 2, + this->width() - insets.width() + corner_radius, + kSplitterHeight); - y += (setViewBounds(title_label_, x, y, max_width, false) + kRowPad); y += (setViewBounds(username_field_, x, y, width, true) + kRowPad); - y += (setViewBounds(password_field_, x, y, width, true) + kRowPad); + y += (setViewBounds(password_field_, x, y, width, true) + 3 * kRowPad); int throbber_y = y; y += (setViewBounds(sign_in_button_, x, y, width, false) + kRowPad); setViewBounds(throbber_, @@ -299,10 +385,13 @@ void NewUserView::Layout() { throbber_->GetPreferredSize().height()) / 2, width, false); - y += setViewBounds(create_account_link_, x, y, max_width, false); - y += setViewBounds(cant_access_account_link_, x, y, max_width, false); - y += setViewBounds(browse_without_signin_link_, x, y, max_width, false); + if (need_create_account_) { + y += setViewBounds(create_account_link_, x, y, max_width, false); + } + if (need_browse_without_signin_) { + y += setViewBounds(browse_without_signin_link_, x, y, max_width, false); + } SchedulePaint(); } @@ -338,8 +427,8 @@ void NewUserView::Login() { } // Sign in button causes a login attempt. -void NewUserView::ButtonPressed( - views::Button* sender, const views::Event& event) { +void NewUserView::ButtonPressed(views::Button* sender, + const views::Event& event) { DCHECK(sender == sign_in_button_); Login(); } @@ -349,12 +438,6 @@ void NewUserView::LinkActivated(views::Link* source, int event_flags) { delegate_->OnCreateAccount(); } else if (source == browse_without_signin_link_) { delegate_->OnLoginOffTheRecord(); - } else if (source == cant_access_account_link_) { - // TODO(nkostylev): Display offline help when network is not connected. - // http://crosbug.com/3874 - delegate_->AddStartUrl( - google_util::AppendGoogleLocaleParam(GURL(kAccountRecoveryHelpUrl))); - delegate_->OnLoginOffTheRecord(); } } @@ -366,12 +449,21 @@ void NewUserView::ClearAndEnablePassword() { throbber_->Stop(); } +void NewUserView::ClearAndEnableFields() { + login_in_process_ = false; + EnableInputControls(true); + SetUsername(std::string()); + SetPassword(std::string()); + username_field_->RequestFocus(); + throbber_->Stop(); +} + gfx::Rect NewUserView::GetPasswordBounds() const { - gfx::Rect screen_bounds(password_field_->bounds()); - gfx::Point origin(screen_bounds.origin()); - views::View::ConvertPointToScreen(password_field_->GetParent(), &origin); - screen_bounds.set_origin(origin); - return screen_bounds; + return password_field_->GetScreenBounds(); +} + +gfx::Rect NewUserView::GetUsernameBounds() const { + return username_field_->GetScreenBounds(); } void NewUserView::StopThrobber() { @@ -383,26 +475,39 @@ bool NewUserView::HandleKeystroke(views::Textfield* s, if (!CrosLibrary::Get()->EnsureLoaded() || login_in_process_) return false; - if (keystroke.GetKeyboardCode() == base::VKEY_RETURN) { + if (keystroke.GetKeyboardCode() == app::VKEY_RETURN) { Login(); // Return true so that processing ends return true; - } else { - delegate_->ClearErrors(); - return false; + } else if (keystroke.GetKeyboardCode() == app::VKEY_LEFT) { + if (s == username_field_ && + username_field_->text().empty() && + password_field_->text().empty()) { + delegate_->NavigateAway(); + return true; + } } + delegate_->ClearErrors(); // Return false so that processing does not end return false; } +void NewUserView::ContentsChanged(views::Textfield* sender, + const string16& new_contents) { + UpdateSignInButtonState(); +} + void NewUserView::EnableInputControls(bool enabled) { languages_menubutton_->SetEnabled(enabled); username_field_->SetEnabled(enabled); password_field_->SetEnabled(enabled); sign_in_button_->SetEnabled(enabled); - create_account_link_->SetEnabled(enabled); - cant_access_account_link_->SetEnabled(enabled); - browse_without_signin_link_->SetEnabled(enabled); + if (need_create_account_) { + create_account_link_->SetEnabled(enabled); + } + if (need_browse_without_signin_) { + browse_without_signin_link_->SetEnabled(enabled); + } } void NewUserView::InitLink(views::Link** link) { diff --git a/chrome/browser/chromeos/login/new_user_view.h b/chrome/browser/chromeos/login/new_user_view.h index bd3b20b..758d587 100644 --- a/chrome/browser/chromeos/login/new_user_view.h +++ b/chrome/browser/chromeos/login/new_user_view.h @@ -4,13 +4,10 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_NEW_USER_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_NEW_USER_VIEW_H_ +#pragma once #include <string> -#include <vector> -#include "base/gtest_prod_util.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" #include "chrome/browser/chromeos/login/language_switch_menu.h" #include "views/accelerator.h" #include "views/controls/button/button.h" @@ -54,10 +51,16 @@ class NewUserView : public views::View, // User started typing so clear all error messages. virtual void ClearErrors() = 0; + + // User tries to navigate away from NewUserView pod. + virtual void NavigateAway() = 0; }; // If |need_border| is true, RoundedRect border and background are required. - NewUserView(Delegate* delegate, bool need_border); + NewUserView(Delegate* delegate, + bool need_border, + bool need_browse_without_signin); + virtual ~NewUserView(); // Initialize view layout. @@ -69,12 +72,18 @@ class NewUserView : public views::View, // Resets password text and sets the enabled state of the password. void ClearAndEnablePassword(); + // Resets password and username text and focuses on username. + void ClearAndEnableFields(); + // Stops throbber shown during login. void StopThrobber(); // Returns bounds of password field in screen coordinates. gfx::Rect GetPasswordBounds() const; + // Returns bounds of username field in screen coordinates. + gfx::Rect GetUsernameBounds() const; + // Overridden from views::View: virtual gfx::Size GetPreferredSize(); virtual void Layout(); @@ -92,7 +101,7 @@ class NewUserView : public views::View, virtual bool HandleKeystroke(views::Textfield* sender, const views::Textfield::Keystroke& keystroke); virtual void ContentsChanged(views::Textfield* sender, - const string16& new_contents) {} + const string16& new_contents); // Overridden from views::ButtonListener. virtual void ButtonPressed(views::Button* sender, const views::Event& event); @@ -104,12 +113,14 @@ class NewUserView : public views::View, protected: // views::View overrides: - virtual void ViewHierarchyChanged(bool is_add, views::View *parent, + virtual void ViewHierarchyChanged(bool is_add, + views::View *parent, views::View *child); virtual void NativeViewHierarchyChanged(bool attached, gfx::NativeView native_view, views::RootView* root_view); - virtual void LocaleChanged(); + virtual void OnLocaleChanged(); + void AddChildView(View* view); private: // Enables/disables input controls (textfields, buttons). @@ -120,24 +131,33 @@ class NewUserView : public views::View, void InitLink(views::Link** link); // Delete and recreate native controls that fail to update preferred size - // after string update. - void RecreateNativeControls(); + // after text/locale update. + void RecreatePeculiarControls(); + + // Enable or disable the |sign_in_button_| based on the contents of the + // |username_field_| and |password_field_|. If there is text in both the + // button is enabled, otherwise it's disabled. + void UpdateSignInButtonState(); // Screen controls. - // NOTE: When adding new controls check RecreateNativeControls() - // that |sign_in_button_| is added with correct index. + // NOTE: sign_in_button_ and languages_menubutton_ are handled with + // special care: they are recreated on any text/locale change + // because they are not resized properly. views::Textfield* username_field_; views::Textfield* password_field_; views::Label* title_label_; + views::Label* title_hint_label_; + views::View* splitter_; views::NativeButton* sign_in_button_; views::Link* create_account_link_; - views::Link* cant_access_account_link_; views::Link* browse_without_signin_link_; views::MenuButton* languages_menubutton_; views::Throbber* throbber_; - views::Accelerator accel_focus_user_; views::Accelerator accel_focus_pass_; + views::Accelerator accel_focus_user_; + views::Accelerator accel_login_off_the_record_; + views::Accelerator accel_enable_accessibility_; // Notifications receiver. Delegate* delegate_; @@ -156,6 +176,17 @@ class NewUserView : public views::View, // If true, this view needs RoundedRect border and background. bool need_border_; + // Whether browse without signin is needed. + bool need_browse_without_signin_; + + // Whether create account link is needed. Set to false for now but we may + // need it back in near future. + bool need_create_account_; + + // Ordinal position of controls inside view layout. + int languages_menubutton_order_; + int sign_in_button_order_; + FRIEND_TEST_ALL_PREFIXES(LoginScreenTest, IncognitoLogin); friend class LoginScreenTest; diff --git a/chrome/browser/chromeos/login/owner_key_utils.cc b/chrome/browser/chromeos/login/owner_key_utils.cc index 916d9c7..3016561 100644 --- a/chrome/browser/chromeos/login/owner_key_utils.cc +++ b/chrome/browser/chromeos/login/owner_key_utils.cc @@ -12,58 +12,79 @@ #include <limits> +#include "base/crypto/rsa_private_key.h" +#include "base/crypto/signature_creator.h" +#include "base/crypto/signature_verifier.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" -#include "base/nss_util_internal.h" #include "base/nss_util.h" +#include "base/nss_util_internal.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/login_library.h" +#include "chrome/common/extensions/extension_constants.h" + +using base::RSAPrivateKey; +using extension_misc::kSignatureAlgorithm; + +namespace chromeos { + +/////////////////////////////////////////////////////////////////////////// +// OwnerKeyUtils // static OwnerKeyUtils::Factory* OwnerKeyUtils::factory_ = NULL; +OwnerKeyUtils::OwnerKeyUtils() {} + +OwnerKeyUtils::~OwnerKeyUtils() {} + +/////////////////////////////////////////////////////////////////////////// +// OwnerKeyUtilsImpl + class OwnerKeyUtilsImpl : public OwnerKeyUtils { public: OwnerKeyUtilsImpl(); - virtual ~OwnerKeyUtilsImpl(); - bool GenerateKeyPair(SECKEYPrivateKey** private_key_out, - SECKEYPublicKey** public_key_out); + RSAPrivateKey* GenerateKeyPair(); + + bool ExportPublicKeyViaDbus(RSAPrivateKey* pair, + LoginLibrary::Delegate* d); + + bool ExportPublicKeyToFile(RSAPrivateKey* pair, const FilePath& key_file); + + bool ImportPublicKey(const FilePath& key_file, + std::vector<uint8>* output); + + bool Verify(const std::string& data, + const std::vector<uint8> signature, + const std::vector<uint8> public_key); + + bool Sign(const std::string& data, + std::vector<uint8>* OUT_signature, + base::RSAPrivateKey* key); + + RSAPrivateKey* FindPrivateKey(const std::vector<uint8>& key); - bool ExportPublicKey(SECKEYPublicKey* key, const FilePath& key_file); + FilePath GetOwnerKeyFilePath(); - SECKEYPublicKey* ImportPublicKey(const FilePath& key_file); + protected: + virtual ~OwnerKeyUtilsImpl(); private: - // Fills in fields of |key_der| with DER encoded data from a file at - // |key_file|. The caller must pass in a pointer to an actual SECItem - // struct for |key_der|. |key_der->data| should be initialized to NULL - // and |key_der->len| should be set to 0. - // - // Upon success, data is stored in key_der->data, and the caller takes - // ownership. Returns false on error. - // - // To free the data, call - // SECITEM_FreeItem(key_der, PR_FALSE); - static bool ReadDERFromFile(const FilePath& key_file, SECItem* key_der); - - // The place outside the owner's encrypted home directory where her + // The file outside the owner's encrypted home directory where her // key will live. static const char kOwnerKeyFile[]; // Key generation parameters. - static const uint32 kKeyGenMechanism; // used by PK11_GenerateKeyPair() - static const unsigned long kExponent; - static const int kKeySizeInBits; + static const uint16 kKeySizeInBits; DISALLOW_COPY_AND_ASSIGN(OwnerKeyUtilsImpl); }; -OwnerKeyUtils::OwnerKeyUtils() {} - -OwnerKeyUtils::~OwnerKeyUtils() {} - +// Defined here, instead of up above, because we need OwnerKeyUtilsImpl. OwnerKeyUtils* OwnerKeyUtils::Create() { if (!factory_) return new OwnerKeyUtilsImpl(); @@ -76,162 +97,63 @@ const char OwnerKeyUtilsImpl::kOwnerKeyFile[] = "/var/lib/whitelist/owner.key"; // We're generating and using 2048-bit RSA keys. // static -const uint32 OwnerKeyUtilsImpl::kKeyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; -// static -const unsigned long OwnerKeyUtilsImpl::kExponent = 65537UL; -// static -const int OwnerKeyUtilsImpl::kKeySizeInBits = 2048; - -OwnerKeyUtilsImpl::OwnerKeyUtilsImpl(){} - -OwnerKeyUtilsImpl::~OwnerKeyUtilsImpl() {} - -bool OwnerKeyUtilsImpl::GenerateKeyPair(SECKEYPrivateKey** private_key_out, - SECKEYPublicKey** public_key_out) { - DCHECK(private_key_out); - DCHECK(public_key_out); - - *private_key_out = NULL; - *public_key_out = NULL; - - // Temporary structures used for generating the result - // in the right format. - PK11SlotInfo* slot = NULL; - PK11RSAGenParams rsa_key_gen_params; - void* key_gen_params; - - bool is_success = true; // Set to false as soon as a step fails. - - rsa_key_gen_params.keySizeInBits = kKeySizeInBits; - rsa_key_gen_params.pe = kExponent; - key_gen_params = &rsa_key_gen_params; +const uint16 OwnerKeyUtilsImpl::kKeySizeInBits = 2048; +OwnerKeyUtilsImpl::OwnerKeyUtilsImpl() { // Ensure NSS is initialized. base::EnsureNSSInit(); +} - slot = base::GetDefaultNSSKeySlot(); - if (!slot) { - LOG(ERROR) << "Couldn't get Internal key slot!"; - is_success = false; - goto failure; - } +OwnerKeyUtilsImpl::~OwnerKeyUtilsImpl() {} - // Need to make sure that the token was initialized. - // Assume a null password. - if (SECSuccess != PK11_Authenticate(slot, PR_TRUE, NULL)) { - LOG(ERROR) << "Couldn't initialze PK11 token!"; - is_success = false; - goto failure; - } +RSAPrivateKey* OwnerKeyUtilsImpl::GenerateKeyPair() { + return RSAPrivateKey::CreateSensitive(kKeySizeInBits); +} - LOG(INFO) << "Creating key pair..."; - { - base::AutoNSSWriteLock lock; - *private_key_out = PK11_GenerateKeyPair(slot, - kKeyGenMechanism, - key_gen_params, - public_key_out, - PR_TRUE, // isPermanent? - PR_TRUE, // isSensitive? - NULL); - } - LOG(INFO) << "done."; +bool OwnerKeyUtilsImpl::ExportPublicKeyViaDbus(RSAPrivateKey* pair, + LoginLibrary::Delegate* d) { + DCHECK(pair); + bool ok = false; - if (!*private_key_out) { - LOG(INFO) << "Generation of Keypair failed!"; - is_success = false; - goto failure; + std::vector<uint8> to_export; + if (!pair->ExportPublicKey(&to_export)) { + LOG(ERROR) << "Formatting key for export via dbus failed!"; + return false; } - failure: - if (!is_success) { - LOG(ERROR) << "Owner key generation failed! (NSS error code " - << PR_GetError() << ")"; - // Do cleanups - base::AutoNSSWriteLock lock; - if (*private_key_out) { - PK11_DestroyTokenObject((*private_key_out)->pkcs11Slot, - (*private_key_out)->pkcs11ID); - SECKEY_DestroyPrivateKey(*private_key_out); - } - if (*public_key_out) { - PK11_DestroyTokenObject((*public_key_out)->pkcs11Slot, - (*public_key_out)->pkcs11ID); - SECKEY_DestroyPublicKey(*public_key_out); - } - } else { - LOG(INFO) << "Owner key generation succeeded!"; - } - if (slot != NULL) { - PK11_FreeSlot(slot); - } + if (CrosLibrary::Get()->EnsureLoaded()) + ok = CrosLibrary::Get()->GetLoginLibrary()->SetOwnerKeyAsync(to_export, d); - return is_success; + return ok; } -bool OwnerKeyUtilsImpl::ExportPublicKey(SECKEYPublicKey* key, - const FilePath& key_file) { - DCHECK(key); - SECItem* der; +bool OwnerKeyUtilsImpl::ExportPublicKeyToFile(RSAPrivateKey* pair, + const FilePath& key_file) { + DCHECK(pair); bool ok = false; int safe_file_size = 0; - // Instead of exporting/importing the key directly, I'm actually - // going to use a SubjectPublicKeyInfo. The reason is because NSS - // exports functions that encode/decode these kinds of structures, while - // it does not export the ones that deal directly with public keys. - der = SECKEY_EncodeDERSubjectPublicKeyInfo(key); - if (!der) { - LOG(ERROR) << "Could not encode public key for export!"; + std::vector<uint8> to_export; + if (!pair->ExportPublicKey(&to_export)) { + LOG(ERROR) << "Formatting key for export failed!"; return false; } - if (der->len > static_cast<uint>(INT_MAX)) { - LOG(ERROR) << "key is too big! " << der->len; + if (to_export.size() > static_cast<uint>(INT_MAX)) { + LOG(ERROR) << "key is too big! " << to_export.size(); } else { - safe_file_size = static_cast<int>(der->len); + safe_file_size = static_cast<int>(to_export.size()); ok = (safe_file_size == file_util::WriteFile(key_file, - reinterpret_cast<char*>(der->data), - der->len)); + reinterpret_cast<char*>(&to_export.front()), + safe_file_size)); } - SECITEM_FreeItem(der, PR_TRUE); return ok; } -SECKEYPublicKey* OwnerKeyUtilsImpl::ImportPublicKey(const FilePath& key_file) { - SECItem key_der; - - if (!ReadDERFromFile(key_file, &key_der)) { - PLOG(ERROR) << "Could not read in key from " << key_file.value() << ":"; - return NULL; - } - - // See the comment in ExportPublicKey() for why I wrote a - // SubjectPublicKeyInfo to disk instead of a key. - CERTSubjectPublicKeyInfo* spki = - SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der); - if (!spki) { - LOG(ERROR) << "Could not decode key info: " << PR_GetError(); - SECITEM_FreeItem(&key_der, PR_FALSE); - return NULL; - } - - SECKEYPublicKey *public_key = SECKEY_ExtractPublicKey(spki); - SECKEY_DestroySubjectPublicKeyInfo(spki); - SECITEM_FreeItem(&key_der, PR_FALSE); - return public_key; -} - -// static -bool OwnerKeyUtilsImpl::ReadDERFromFile(const FilePath& key_file, - SECItem* key_der) { - // I'd use NSS' SECU_ReadDERFromFile() here, but the SECU_* functions are - // considered internal to the NSS command line utils. - // This code is lifted, in spirit, from the implementation of that function. - DCHECK(key_der) << "Don't pass NULL for |key_der|"; - +bool OwnerKeyUtilsImpl::ImportPublicKey(const FilePath& key_file, + std::vector<uint8>* output) { // Get the file size (must fit in a 32 bit int for NSS). int64 file_size; if (!file_util::GetFileSize(key_file, &file_size)) { @@ -245,23 +167,48 @@ bool OwnerKeyUtilsImpl::ReadDERFromFile(const FilePath& key_file, } int32 safe_file_size = static_cast<int32>(file_size); - // Allocate space for the DER encoded data. - key_der->data = NULL; - if (!SECITEM_AllocItem(NULL, key_der, safe_file_size)) { - LOG(ERROR) << "Could not create space for DER encoded key"; - return false; - } - + output->resize(safe_file_size); // Get the key data off of disk int data_read = file_util::ReadFile(key_file, - reinterpret_cast<char*>(key_der->data), + reinterpret_cast<char*>(&(output->at(0))), safe_file_size); + return data_read == safe_file_size; +} + +bool OwnerKeyUtilsImpl::Verify(const std::string& data, + const std::vector<uint8> signature, + const std::vector<uint8> public_key) { + base::SignatureVerifier verifier; + if (!verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm), + &signature[0], signature.size(), + &public_key[0], public_key.size())) { + return false; + } + + verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()), + data.length()); + return (verifier.VerifyFinal()); +} - if (data_read != safe_file_size) { - LOG(ERROR) << "Read the wrong amount of data from the DER encoded key! " - << data_read; - SECITEM_FreeItem(key_der, PR_FALSE); +bool OwnerKeyUtilsImpl::Sign(const std::string& data, + std::vector<uint8>* OUT_signature, + base::RSAPrivateKey* key) { + scoped_ptr<base::SignatureCreator> signer( + base::SignatureCreator::Create(key)); + if (!signer->Update(reinterpret_cast<const uint8*>(data.c_str()), + data.length())) { return false; } - return true; + return signer->Final(OUT_signature); +} + +RSAPrivateKey* OwnerKeyUtilsImpl::FindPrivateKey( + const std::vector<uint8>& key) { + return RSAPrivateKey::FindFromPublicKeyInfo(key); +} + +FilePath OwnerKeyUtilsImpl::GetOwnerKeyFilePath() { + return FilePath(OwnerKeyUtilsImpl::kOwnerKeyFile); } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/owner_key_utils.h b/chrome/browser/chromeos/login/owner_key_utils.h index 8900ed4..4921745 100644 --- a/chrome/browser/chromeos/login/owner_key_utils.h +++ b/chrome/browser/chromeos/login/owner_key_utils.h @@ -4,21 +4,23 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_KEY_UTILS_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_KEY_UTILS_H_ +#pragma once + +#include <vector> #include "base/basictypes.h" +#include "base/ref_counted.h" +#include "chrome/browser/chromeos/cros/login_library.h" -// Forward declarations of NSS data structures. -struct SECKEYPrivateKeyStr; -struct SECKEYPublicKeyStr; -struct SECItemStr; +class FilePath; -typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey; -typedef struct SECKEYPublicKeyStr SECKEYPublicKey; -typedef struct SECItemStr SECItem; +namespace base { +class RSAPrivateKey; +} -class FilePath; +namespace chromeos { -class OwnerKeyUtils { +class OwnerKeyUtils : public base::RefCounted<OwnerKeyUtils> { public: class Factory { public: @@ -26,7 +28,6 @@ class OwnerKeyUtils { }; OwnerKeyUtils(); - virtual ~OwnerKeyUtils(); // Sets the factory used by the static method Create to create an // OwnerKeyUtils. OwnerKeyUtils does not take ownership of @@ -42,29 +43,59 @@ class OwnerKeyUtils { // Generate a public/private RSA keypair and store them in the NSS database. // The keys will be kKeySizeInBits in length (Recommend >= 2048 bits). + // The caller takes ownership. // - // Returns false on error. - // - // The caller takes ownership of both objects, which are allocated by libnss. - // To free them, call - // SECKEY_DestroyPrivateKey(*private_key_out); - // SECKEY_DestroyPublicKey(*public_key_out); - virtual bool GenerateKeyPair(SECKEYPrivateKey** private_key_out, - SECKEYPublicKey** public_key_out) = 0; - - // DER encodes |key| and writes it out to |key_file|. + // Returns NULL on error. + virtual base::RSAPrivateKey* GenerateKeyPair() = 0; + + // DER encodes public half of |pair| and asynchronously exports it via DBus. + // The data sent is a DER-encoded X509 SubjectPublicKeyInfo object. + // Returns false on error, true if the attempt is successfully begun. + // d->Run() will be called with a boolean indicating success or failure when + // the attempt is complete. + virtual bool ExportPublicKeyViaDbus(base::RSAPrivateKey* pair, + LoginLibrary::Delegate* d) = 0; + + // DER encodes public half of |pair| and writes it out to |key_file|. // The blob on disk is a DER-encoded X509 SubjectPublicKeyInfo object. // Returns false on error. - virtual bool ExportPublicKey(SECKEYPublicKey* key, - const FilePath& key_file) = 0; + virtual bool ExportPublicKeyToFile(base::RSAPrivateKey* pair, + const FilePath& key_file) = 0; // Assumes that the file at |key_file| exists. - // Caller takes ownership of returned object; returns NULL on error. - // To free, call SECKEY_DestroyPublicKey. - virtual SECKEYPublicKey* ImportPublicKey(const FilePath& key_file) = 0; + // Upon success, returns true and populates |output|. False on failure. + virtual bool ImportPublicKey(const FilePath& key_file, + std::vector<uint8>* output) = 0; + + // Verfiy that |signature| is a Sha1-with-RSA signature over |data| with + // |public_key| + // Returns true if so, false on bad signature or other error. + virtual bool Verify(const std::string& data, + const std::vector<uint8> signature, + const std::vector<uint8> public_key) = 0; + + // Sign |data| with |key| using Sha1 with RSA. If successful, return true + // and populate |OUT_signature|. + virtual bool Sign(const std::string& data, + std::vector<uint8>* OUT_signature, + base::RSAPrivateKey* key) = 0; + + // Looks for the private key associated with |key| in the default slot, + // and returns it if it can be found. Returns NULL otherwise. + // Caller takes ownership. + virtual base::RSAPrivateKey* FindPrivateKey( + const std::vector<uint8>& key) = 0; + + virtual FilePath GetOwnerKeyFilePath() = 0; + + protected: + virtual ~OwnerKeyUtils(); private: + friend class base::RefCounted<OwnerKeyUtils>; static Factory* factory_; }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_KEY_UTILS_H_ diff --git a/chrome/browser/chromeos/login/owner_key_utils_unittest.cc b/chrome/browser/chromeos/login/owner_key_utils_unittest.cc index 7a7e9e7..3b21466 100644 --- a/chrome/browser/chromeos/login/owner_key_utils_unittest.cc +++ b/chrome/browser/chromeos/login/owner_key_utils_unittest.cc @@ -4,112 +4,57 @@ #include "chrome/browser/chromeos/login/owner_key_utils.h" -#include <cert.h> -#include <keyhi.h> -#include <keythi.h> // KeyType enum -#include <pk11pub.h> -#include <stdlib.h> - #include <string> +#include <vector> +#include "base/crypto/rsa_private_key.h" #include "base/file_path.h" #include "base/file_util.h" -#include "base/logging.h" -#include "base/nss_util_internal.h" #include "base/nss_util.h" -#include "base/scoped_ptr.h" +#include "base/nss_util_internal.h" +#include "base/ref_counted.h" #include "base/scoped_temp_dir.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chromeos { class OwnerKeyUtilsTest : public ::testing::Test { public: - OwnerKeyUtilsTest() - : private_key_(NULL), - public_key_(NULL), - utils_(OwnerKeyUtils::Create()) { - - } + OwnerKeyUtilsTest() : utils_(OwnerKeyUtils::Create()) {} virtual ~OwnerKeyUtilsTest() {} virtual void SetUp() { base::OpenPersistentNSSDB(); } - virtual void TearDown() { - if (private_key_) { - PK11_DestroyTokenObject(private_key_->pkcs11Slot, private_key_->pkcs11ID); - SECKEY_DestroyPrivateKey(private_key_); - } - if (public_key_) { - PK11_DestroyTokenObject(public_key_->pkcs11Slot, public_key_->pkcs11ID); - SECKEY_DestroyPublicKey(public_key_); - } - } - - SECKEYPrivateKey* private_key_; - SECKEYPublicKey* public_key_; - scoped_ptr<OwnerKeyUtils> utils_; + scoped_refptr<OwnerKeyUtils> utils_; }; -TEST_F(OwnerKeyUtilsTest, KeyGenerate) { - EXPECT_TRUE(utils_->GenerateKeyPair(&private_key_, &public_key_)); - EXPECT_TRUE(private_key_ != NULL); - ASSERT_TRUE(public_key_ != NULL); - EXPECT_EQ(public_key_->keyType, rsaKey); -} - TEST_F(OwnerKeyUtilsTest, ExportImportPublicKey) { - EXPECT_TRUE(utils_->GenerateKeyPair(&private_key_, &public_key_)); + scoped_ptr<base::RSAPrivateKey> pair(utils_->GenerateKeyPair()); + ASSERT_NE(pair.get(), reinterpret_cast<base::RSAPrivateKey*>(NULL)); + // Export public key to file. ScopedTempDir tmpdir; FilePath tmpfile; ASSERT_TRUE(tmpdir.CreateUniqueTempDir()); ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir.path(), &tmpfile)); - - EXPECT_TRUE(utils_->ExportPublicKey(public_key_, tmpfile)); - - // Now, verify that we can look up the private key, given the public key - // we exported. We'll create - // an ID from the key, and then use that ID to query the token in the - // default slot for a matching private key. Then we'll make sure it's - // the same as |private_key_| - PK11SlotInfo* slot = NULL; - SECItem* ck_id = NULL; - SECKEYPublicKey* from_disk = NULL; - SECKEYPrivateKey* found = NULL; - - slot = base::GetDefaultNSSKeySlot(); - EXPECT_TRUE(slot != NULL); - if (NULL == slot) - goto cleanup; - - from_disk = utils_->ImportPublicKey(tmpfile); - ASSERT_TRUE(from_disk != NULL); - - ck_id = PK11_MakeIDFromPubKey(&(from_disk->u.rsa.modulus)); - EXPECT_TRUE(ck_id != NULL); - if (NULL == ck_id) - goto cleanup; - - found = PK11_FindKeyByKeyID(slot, ck_id, NULL); - EXPECT_TRUE(found != NULL); - if (NULL == found) - goto cleanup; - - EXPECT_EQ(private_key_->pkcs11ID, found->pkcs11ID); - - cleanup: - if (slot) - PK11_FreeSlot(slot); - if (from_disk) - SECKEY_DestroyPublicKey(from_disk); - if (found) - SECKEY_DestroyPrivateKey(found); - if (ck_id) - SECITEM_ZfreeItem(ck_id, PR_TRUE); + ASSERT_TRUE(utils_->ExportPublicKeyToFile(pair.get(), tmpfile)); + + // Export public key, so that we can compare it to the one we get off disk. + std::vector<uint8> public_key; + ASSERT_TRUE(pair->ExportPublicKey(&public_key)); + std::vector<uint8> from_disk; + ASSERT_TRUE(utils_->ImportPublicKey(tmpfile, &from_disk)); + + std::vector<uint8>::iterator pubkey_it; + std::vector<uint8>::iterator disk_it; + for (pubkey_it = public_key.begin(), disk_it = from_disk.begin(); + pubkey_it < public_key.end(); + pubkey_it++, disk_it++) { + EXPECT_EQ(*pubkey_it, *disk_it); + } } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/password_changed_view.cc b/chrome/browser/chromeos/login/password_changed_view.cc index ddb36ae..87c89d5 100644 --- a/chrome/browser/chromeos/login/password_changed_view.cc +++ b/chrome/browser/chromeos/login/password_changed_view.cc @@ -4,11 +4,12 @@ #include "chrome/browser/chromeos/login/password_changed_view.h" +#include "app/keyboard_codes.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" -#include "base/keyboard_codes.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "views/controls/button/radio_button.h" @@ -60,8 +61,10 @@ gfx::Size PasswordChangedView::GetPreferredSize() { void PasswordChangedView::ViewHierarchyChanged(bool is_add, views::View* parent, views::View* child) { - if (is_add && child == this) + if (is_add && child == this) { Init(); + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); + } } void PasswordChangedView::Init() { @@ -162,7 +165,7 @@ void PasswordChangedView::ButtonPressed(Button* sender, bool PasswordChangedView::HandleKeystroke(views::Textfield* s, const views::Textfield::Keystroke& keystroke) { - if (keystroke.GetKeyboardCode() == base::VKEY_RETURN) { + if (keystroke.GetKeyboardCode() == app::VKEY_RETURN) { ExitDialog(); return true; } diff --git a/chrome/browser/chromeos/login/password_changed_view.h b/chrome/browser/chromeos/login/password_changed_view.h index 535a05a..48f6ca3 100644 --- a/chrome/browser/chromeos/login/password_changed_view.h +++ b/chrome/browser/chromeos/login/password_changed_view.h @@ -4,10 +4,10 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_PASSWORD_CHANGED_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_PASSWORD_CHANGED_VIEW_H_ +#pragma once #include <string> -#include "app/message_box_flags.h" #include "views/controls/button/button.h" #include "views/controls/textfield/textfield.h" #include "views/view.h" diff --git a/chrome/browser/chromeos/login/registration_screen.cc b/chrome/browser/chromeos/login/registration_screen.cc index 40348d7..5c60331 100644 --- a/chrome/browser/chromeos/login/registration_screen.cc +++ b/chrome/browser/chromeos/login/registration_screen.cc @@ -17,6 +17,7 @@ #include "googleurl/src/gurl.h" #include "net/url_request/url_request_about_job.h" #include "net/url_request/url_request_filter.h" +#include "views/widget/widget_gtk.h" namespace chromeos { @@ -86,15 +87,14 @@ void RegistrationScreen::OnPageLoaded() { if (g_browser_process) { const std::string locale = g_browser_process->GetApplicationLocale(); input_method::EnableInputMethods( - locale, input_method::kKeyboardLayoutsOnly, ""); - // TODO(yusukes,suzhe): Change the 2nd argument to kAllInputMethods when - // crosbug.com/2670 is fixed. + locale, input_method::kAllInputMethods, ""); } view()->ShowPageContent(); } void RegistrationScreen::OnPageLoadFailed(const std::string& url) { - CloseScreen(ScreenObserver::CONNECTION_FAILED); + LOG(ERROR) << "Error loading registration page: " << url; + CloseScreen(ScreenObserver::REGISTRATION_SKIPPED); } /////////////////////////////////////////////////////////////////////////////// @@ -106,9 +106,11 @@ void RegistrationScreen::OnPageLoadFailed(const std::string& url) { PageTransition::Type transition) { if (url.spec() == kRegistrationSuccessUrl) { source->Stop(); + LOG(INFO) << "Registration form completed."; CloseScreen(ScreenObserver::REGISTRATION_SUCCESS); } else if (url.spec() == kRegistrationSkippedUrl) { source->Stop(); + LOG(INFO) << "Registration form skipped."; CloseScreen(ScreenObserver::REGISTRATION_SKIPPED); } else { source->Stop(); @@ -118,6 +120,13 @@ void RegistrationScreen::OnPageLoadFailed(const std::string& url) { } } +void RegistrationScreen::HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) { + views::Widget* widget = view()->GetWidget(); + if (widget && event.os_event && !event.skip_in_browser) + static_cast<views::WidgetGtk*>(widget)->HandleKeyboardEvent(event.os_event); +} + /////////////////////////////////////////////////////////////////////////////// // RegistrationScreen, private: void RegistrationScreen::CloseScreen(ScreenObserver::ExitCodes code) { diff --git a/chrome/browser/chromeos/login/registration_screen.h b/chrome/browser/chromeos/login/registration_screen.h index 89d1cdb..6c10f9f 100644 --- a/chrome/browser/chromeos/login/registration_screen.h +++ b/chrome/browser/chromeos/login/registration_screen.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_REGISTRATION_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_REGISTRATION_SCREEN_H_ +#pragma once #include <string> @@ -91,6 +92,7 @@ class RegistrationScreen : public ViewScreen<RegistrationView>, const GURL& referrer, WindowOpenDisposition disposition, PageTransition::Type transition); + virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event); // WebPageScreen implementation: virtual void CloseScreen(ScreenObserver::ExitCodes code); diff --git a/chrome/browser/chromeos/login/rounded_rect_painter.cc b/chrome/browser/chromeos/login/rounded_rect_painter.cc index 3c2c361..a4878e5 100644 --- a/chrome/browser/chromeos/login/rounded_rect_painter.cc +++ b/chrome/browser/chromeos/login/rounded_rect_painter.cc @@ -4,11 +4,11 @@ #include "chrome/browser/chromeos/login/rounded_rect_painter.h" -#include "app/resource_bundle.h" #include "base/logging.h" #include "gfx/canvas_skia.h" -#include "third_party/skia/include/effects/SkGradientShader.h" +#include "chrome/browser/chromeos/login/helper.h" #include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/effects/SkGradientShader.h" #include "views/border.h" #include "views/painter.h" @@ -16,7 +16,6 @@ namespace chromeos { namespace { -const int kCornerRadius = 5; const SkColor kScreenTopColor = SkColorSetRGB(250, 251, 251); const SkColor kScreenBottomColor = SkColorSetRGB(204, 209, 212); const SkColor kScreenShadowColor = SkColorSetARGB(64, 34, 54, 115); @@ -183,7 +182,48 @@ void RoundedRectBorder::GetInsets(gfx::Insets* insets) const { insets->Set(inset - shadow / 3, inset, inset + shadow / 3, inset); } -} // namespace +// Simple solid round background. +class RoundedBackground : public views::Background { + public: + explicit RoundedBackground(int corner_radius, + int stroke_width, + const SkColor& background_color, + const SkColor& stroke_color) + : corner_radius_(corner_radius), + stroke_width_(stroke_width), + stroke_color_(stroke_color) { + SetNativeControlColor(background_color); + } + + virtual void Paint(gfx::Canvas* canvas, views::View* view) const { + SkRect rect; + rect.iset(0, 0, view->width(), view->height()); + SkPath path; + path.addRoundRect(rect, + SkIntToScalar(corner_radius_), + SkIntToScalar(corner_radius_)); + // Draw interior. + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setColor(get_color()); + canvas->AsCanvasSkia()->drawPath(path, paint); + // Redraw boundary region with correspoinding color. + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(stroke_width_)); + paint.setColor(stroke_color_); + canvas->AsCanvasSkia()->drawPath(path, paint); + } + + private: + int corner_radius_; + int stroke_width_; + SkColor stroke_color_; + + DISALLOW_COPY_AND_ASSIGN(RoundedBackground); +}; + +} // namespace // static const BorderDefinition BorderDefinition::kScreenBorder = { @@ -191,7 +231,17 @@ const BorderDefinition BorderDefinition::kScreenBorder = { SK_ColorBLACK, kScreenShadow, kScreenShadowColor, - kCornerRadius, + login::kScreenCornerRadius, + kScreenTopColor, + kScreenBottomColor +}; + +const BorderDefinition BorderDefinition::kUserBorder = { + 0, + SK_ColorBLACK, + 0, + kScreenShadowColor, + login::kUserCornerRadius, kScreenTopColor, kScreenBottomColor }; @@ -204,4 +254,12 @@ views::Border* CreateWizardBorder(const BorderDefinition* const border) { return new RoundedRectBorder(border); } +views::Background* CreateRoundedBackground(int corner_radius, + int stroke_width, + SkColor background_color, + SkColor stroke_color) { + return new RoundedBackground( + corner_radius, stroke_width, background_color, stroke_color); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/rounded_rect_painter.h b/chrome/browser/chromeos/login/rounded_rect_painter.h index 3788c1b..4bd36ec 100644 --- a/chrome/browser/chromeos/login/rounded_rect_painter.h +++ b/chrome/browser/chromeos/login/rounded_rect_painter.h @@ -4,10 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_ROUNDED_RECT_PAINTER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_ROUNDED_RECT_PAINTER_H_ +#pragma once #include "third_party/skia/include/core/SkColor.h" namespace views { +class Background; class Border; class Painter; } // namespace views @@ -24,6 +26,7 @@ struct BorderDefinition { SkColor bottom_color; static const BorderDefinition kScreenBorder; + static const BorderDefinition kUserBorder; }; // Creates painter to paint view background with parameters specified. @@ -32,6 +35,11 @@ views::Painter* CreateWizardPainter(const BorderDefinition* const border); // that actually draws both border and background. views::Border* CreateWizardBorder(const BorderDefinition* const border); +// Creates simple round background. +views::Background* CreateRoundedBackground(int corner_radius, + int stroke_width, + SkColor background_color, + SkColor stroke_color); } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_LOGIN_ROUNDED_RECT_PAINTER_H_ diff --git a/chrome/browser/chromeos/login/screen_lock_view.cc b/chrome/browser/chromeos/login/screen_lock_view.cc index f387d48..f7d3330 100644 --- a/chrome/browser/chromeos/login/screen_lock_view.cc +++ b/chrome/browser/chromeos/login/screen_lock_view.cc @@ -8,9 +8,12 @@ #include "app/resource_bundle.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/screen_locker.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/user_view.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" +#include "chrome/browser/profile_manager.h" #include "chrome/common/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -25,6 +28,8 @@ namespace chromeos { namespace { +const int kCornerRadius = 5; + // A Textfield for password, which also sets focus to itself // when a mouse is clicked on it. This is necessary in screen locker // as mouse events are grabbed in the screen locker. @@ -64,10 +69,17 @@ void ScreenLockView::Init() { NotificationType::LOGIN_USER_IMAGE_CHANGED, NotificationService::AllSources()); - user_view_ = new UserView(this, false); + user_view_ = new UserView(this, + false, // is_login + true); // need_background views::View* main = new views::View(); + // Use rounded rect background. + views::Painter* painter = + CreateWizardPainter(&BorderDefinition::kUserBorder); + main->set_background( - views::Background::CreateSolidBackground(login::kBackgroundColor)); + views::Background::CreateBackgroundPainter(true, painter)); + main->set_border(CreateWizardBorder(&BorderDefinition::kUserBorder)); // Password field. password_field_ = new PasswordField(); @@ -184,7 +196,7 @@ bool ScreenLockView::HandleKeystroke( views::Textfield* sender, const views::Textfield::Keystroke& keystroke) { screen_locker_->ClearErrors(); - if (keystroke.GetKeyboardCode() == base::VKEY_RETURN) { + if (keystroke.GetKeyboardCode() == app::VKEY_RETURN) { screen_locker_->Authenticate(password_field_->text()); return true; } @@ -205,4 +217,10 @@ void ScreenLockView::Observe( user_view_->SetImage(user->image()); } +void ScreenLockView::ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child) { + if (is_add && this == child) + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); +} } // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_lock_view.h b/chrome/browser/chromeos/login/screen_lock_view.h index eb75d78..e826271 100644 --- a/chrome/browser/chromeos/login/screen_lock_view.h +++ b/chrome/browser/chromeos/login/screen_lock_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCK_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCK_VIEW_H_ +#pragma once #include "chrome/browser/chromeos/login/user_view.h" #include "chrome/common/notification_observer.h" @@ -67,6 +68,12 @@ class ScreenLockView : public views::View, // UserView::Delegate implementation: virtual void OnSignout(); + protected: + // views::View implementation: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + private: friend class test::ScreenLockerTester; diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index ebd120d..382f854 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -9,14 +9,19 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/command_line.h" #include "base/message_loop.h" #include "base/singleton.h" #include "base/string_util.h" +#include "base/timer.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/input_method_library.h" +#include "chrome/browser/chromeos/cros/keyboard_library.h" +#include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/cros/screen_lock_library.h" #include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/login/authenticator.h" @@ -26,10 +31,12 @@ #include "chrome/browser/chromeos/login/screen_lock_view.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/metrics/user_metrics.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" +#include "cros/chromeos_wm_ipc_enums.h" +#include "googleurl/src/gurl.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "cros/chromeos_wm_ipc_enums.h" #include "views/screen.h" #include "views/widget/root_view.h" #include "views/widget/widget_gtk.h" @@ -43,6 +50,9 @@ const int kGrabFailureLimit = 60; // Each keyboard layout has a dummy input method ID which starts with "xkb:". const char kValidInputMethodPrefix[] = "xkb:"; +// A idle time to show the screen saver in seconds. +const int kScreenSaverIdleTimeout = 15; + // Observer to start ScreenLocker when the screen lock class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, public NotificationObserver { @@ -65,6 +75,7 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, } virtual void LockScreen(chromeos::ScreenLockLibrary* obj) { + LOG(INFO) << "In: ScreenLockObserver::LockScreen"; SetupInputMethodsForScreenLocker(); chromeos::ScreenLocker::Show(); } @@ -90,11 +101,21 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, saved_active_input_method_list_.empty()) { chromeos::InputMethodLibrary* language = chromeos::CrosLibrary::Get()->GetInputMethodLibrary(); + chromeos::KeyboardLibrary* keyboard = + chromeos::CrosLibrary::Get()->GetKeyboardLibrary(); + saved_previous_input_method_id_ = language->previous_input_method().id; saved_current_input_method_id_ = language->current_input_method().id; scoped_ptr<chromeos::InputMethodDescriptors> active_input_method_list( language->GetActiveInputMethods()); + const std::string hardware_keyboard = + keyboard->GetHardwareKeyboardLayoutName(); // e.g. "xkb:us::eng" + // We'll add the hardware keyboard if it's not included in + // |active_input_method_list| so that the user can always use the hardware + // keyboard on the screen locker. + bool should_add_hardware_keyboard = true; + chromeos::ImeConfigValue value; value.type = chromeos::ImeConfigValue::kValueTypeStringList; for (size_t i = 0; i < active_input_method_list->size(); ++i) { @@ -105,13 +126,17 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, if (!StartsWithASCII(input_method_id, kValidInputMethodPrefix, true)) continue; value.string_list_value.push_back(input_method_id); + if (input_method_id == hardware_keyboard) { + should_add_hardware_keyboard = false; + } } - if (value.string_list_value.empty()) { - value.string_list_value.push_back(kFallbackInputMethodId); // US qwerty + if (should_add_hardware_keyboard) { + value.string_list_value.push_back(hardware_keyboard); } - language->SetImeConfig(chromeos::kGeneralSectionName, - chromeos::kPreloadEnginesConfigName, - value); + language->SetImeConfig( + chromeos::language_prefs::kGeneralSectionName, + chromeos::language_prefs::kPreloadEnginesConfigName, + value); } } @@ -124,9 +149,10 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, chromeos::ImeConfigValue value; value.type = chromeos::ImeConfigValue::kValueTypeStringList; value.string_list_value = saved_active_input_method_list_; - language->SetImeConfig(chromeos::kGeneralSectionName, - chromeos::kPreloadEnginesConfigName, - value); + language->SetImeConfig( + chromeos::language_prefs::kGeneralSectionName, + chromeos::language_prefs::kPreloadEnginesConfigName, + value); // Send previous input method id first so Ctrl+space would work fine. if (!saved_previous_input_method_id_.empty()) language->ChangeInputMethod(saved_previous_input_method_id_); @@ -169,6 +195,11 @@ class LockWindow : public views::WidgetGtk { return false; } + virtual void OnDestroy(GtkWidget* object) { + LOG(INFO) << "OnDestroy: LockWindow destroyed"; + views::WidgetGtk::OnDestroy(object); + } + virtual void ClearNativeFocus() { DCHECK(toplevel_focus_widget_); gtk_widget_grab_focus(toplevel_focus_widget_); @@ -206,6 +237,11 @@ class GrabWidget : public views::WidgetGtk { virtual void Show() { views::WidgetGtk::Show(); + // Now steal all inputs. + TryGrabAllInputs(); + } + + void ClearGrab() { GtkWidget* current_grab_window; // Grab gtk input first so that the menu holding grab will close itself. gtk_grab_add(window_contents()); @@ -217,9 +253,6 @@ class GrabWidget : public views::WidgetGtk { // until it's empty. while ((current_grab_window = gtk_grab_get_current()) != NULL) gtk_grab_remove(current_grab_window); - - // Now steal all inputs. - TryGrabAllInputs(); } virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) { @@ -256,9 +289,12 @@ class GrabWidget : public views::WidgetGtk { }; void GrabWidget::TryGrabAllInputs() { - if (kbd_grab_status_ != GDK_GRAB_SUCCESS) + ClearGrab(); + + if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, GDK_CURRENT_TIME); + } if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { mouse_grab_status_ = gdk_pointer_grab(window_contents()->window, @@ -273,8 +309,8 @@ void GrabWidget::TryGrabAllInputs() { if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || mouse_grab_status_ != GDK_GRAB_SUCCESS) && grab_failure_count_++ < kGrabFailureLimit) { - DLOG(WARNING) << "Failed to grab inputs. Trying again in 1 second: kbd=" - << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; + LOG(WARNING) << "Failed to grab inputs. Trying again in 1 second: kbd=" + << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; MessageLoop::current()->PostDelayedTask( FROM_HERE, task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), @@ -293,7 +329,7 @@ void GrabWidget::TryGrabAllInputs() { // addition to other background components. class ScreenLockerBackgroundView : public chromeos::BackgroundView { public: - ScreenLockerBackgroundView(views::WidgetGtk* lock_widget) + explicit ScreenLockerBackgroundView(views::WidgetGtk* lock_widget) : lock_widget_(lock_widget) { } @@ -330,23 +366,30 @@ ScreenLocker* ScreenLocker::screen_locker_ = NULL; // See screen_locker.h for more details. class MouseEventRelay : public MessageLoopForUI::Observer { public: - MouseEventRelay(GdkWindow* src, GdkWindow* dest) : src_(src), dest_(dest) { + MouseEventRelay(GdkWindow* src, GdkWindow* dest) + : src_(src), + dest_(dest), + initialized_(false) { DCHECK(src_); DCHECK(dest_); - gint src_x, src_y, dest_x, dest_y, width, height, depth; - gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth); - gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth); - - offset_.SetPoint(dest_x - src_x, dest_y - src_y); } virtual void WillProcessEvent(GdkEvent* event) {} virtual void DidProcessEvent(GdkEvent* event) { - if (event->any.window != src_) { + if (event->any.window != src_) return; + if (!initialized_) { + gint src_x, src_y, dest_x, dest_y, width, height, depth; + gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth); + // wait to compute offset until the info bubble widget's location + // is available. + if (dest_x < 0 || dest_y < 0) + return; + gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth); + offset_.SetPoint(dest_x - src_x, dest_y - src_y); + initialized_ = true; } - if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) { GdkEvent* copy = gdk_event_copy(event); @@ -359,8 +402,8 @@ class MouseEventRelay : public MessageLoopForUI::Observer { gdk_event_free(copy); } else if (event->type == GDK_MOTION_NOTIFY) { GdkEvent* copy = gdk_event_copy(event); - copy->button.window = dest_; - g_object_ref(copy->button.window); + copy->motion.window = dest_; + g_object_ref(copy->motion.window); copy->motion.x -= offset_.x(); copy->motion.y -= offset_.y(); @@ -372,6 +415,7 @@ class MouseEventRelay : public MessageLoopForUI::Observer { private: GdkWindow* src_; GdkWindow* dest_; + bool initialized_; // Offset from src_'s origin to dest_'s origin. gfx::Point offset_; @@ -381,6 +425,7 @@ class MouseEventRelay : public MessageLoopForUI::Observer { // A event observer used to unlock the screen upon user's action // without asking password. Used in BWSI and auto login mode. +// TODO(oshima): consolidate InputEventObserver and LockerInputEventObserver. class InputEventObserver : public MessageLoopForUI::Observer { public: explicit InputEventObserver(ScreenLocker* screen_locker) @@ -411,7 +456,42 @@ class InputEventObserver : public MessageLoopForUI::Observer { DISALLOW_COPY_AND_ASSIGN(InputEventObserver); }; -//////////////////////////////////////////////////////////////////////////////// +// A event observer used to show the screen locker upon +// user action: mouse or keyboard interactions. +// TODO(oshima): this has to be disabled while authenticating. +class LockerInputEventObserver : public MessageLoopForUI::Observer { + public: + explicit LockerInputEventObserver(ScreenLocker* screen_locker) + : screen_locker_(screen_locker), + ALLOW_THIS_IN_INITIALIZER_LIST( + timer_(base::TimeDelta::FromSeconds(kScreenSaverIdleTimeout), this, + &LockerInputEventObserver::StartScreenSaver)) { + } + + virtual void WillProcessEvent(GdkEvent* event) { + if ((event->type == GDK_KEY_PRESS || + event->type == GDK_BUTTON_PRESS || + event->type == GDK_MOTION_NOTIFY)) { + timer_.Reset(); + screen_locker_->StopScreenSaver(); + } + } + + virtual void DidProcessEvent(GdkEvent* event) { + } + + private: + void StartScreenSaver() { + screen_locker_->StartScreenSaver(); + } + + chromeos::ScreenLocker* screen_locker_; + base::DelayTimer<LockerInputEventObserver> timer_; + + DISALLOW_COPY_AND_ASSIGN(LockerInputEventObserver); +}; + +////////////////////////////////////////////////////////////////////////////// // ScreenLocker, public: ScreenLocker::ScreenLocker(const UserManager::User& user) @@ -461,14 +541,20 @@ void ScreenLocker::Init() { lock_widget_->GetRootView()->SetVisible(false); lock_widget_->Show(); - views::View* screen = new ScreenLockerBackgroundView(lock_widget_); + // Configuring the background url. + std::string url_string = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kScreenSaverUrl); + background_view_ = new ScreenLockerBackgroundView(lock_widget_); + background_view_->Init(GURL(url_string)); DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); WmIpc::instance()->SetWindowType( lock_window_->GetNativeView(), WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, NULL); - lock_window_->SetContentsView(screen); + + lock_window_->SetContentsView(background_view_); lock_window_->Show(); // Don't let X draw default background, which was causing flash on @@ -480,7 +566,7 @@ void ScreenLocker::Init() { lock_window->set_toplevel_focus_widget(lock_widget_->window_contents()); } -void ScreenLocker::OnLoginFailure(const std::string& error) { +void ScreenLocker::OnLoginFailure(const LoginFailure& error) { DLOG(INFO) << "OnLoginFailure"; EnableInput(); // Don't enable signout button here as we're showing @@ -494,8 +580,9 @@ void ScreenLocker::OnLoginFailure(const std::string& error) { if (error_info_) error_info_->Close(); std::wstring msg = l10n_util::GetString(IDS_LOGIN_ERROR_AUTHENTICATING); - if (!error.empty()) - msg += L"\n" + ASCIIToWide(error); + const std::string error_text = error.GetErrorString(); + if (!error_text.empty()) + msg += L"\n" + ASCIIToWide(error_text); InputMethodLibrary* input_method_library = CrosLibrary::Get()->GetInputMethodLibrary(); @@ -508,6 +595,7 @@ void ScreenLocker::OnLoginFailure(const std::string& error) { BubbleBorder::BOTTOM_LEFT, ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), msg, + std::wstring(), // TODO: add help link this); if (mouse_event_relay_.get()) { MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get()); @@ -520,9 +608,7 @@ void ScreenLocker::OnLoginFailure(const std::string& error) { void ScreenLocker::OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& unused) { - - DLOG(INFO) << "OnLoginSuccess"; - + LOG(INFO) << "OnLoginSuccess: Sending Unlock request."; if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenUnlockRequested(); } @@ -565,7 +651,9 @@ void ScreenLocker::EnableInput() { void ScreenLocker::Signout() { if (!error_info_) { // TODO(oshima): record this action in user metrics. - BrowserList::CloseAllBrowsersAndExit(); + if (CrosLibrary::Get()->EnsureLoaded()) { + CrosLibrary::Get()->GetLoginLibrary()->StopSession(""); + } // Don't hide yet the locker because the chrome screen may become visible // briefly. @@ -581,25 +669,27 @@ void ScreenLocker::OnGrabInputs() { // static void ScreenLocker::Show() { + LOG(INFO) << "In ScreenLocker::Show"; DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); // Exit fullscreen. Browser* browser = BrowserList::GetLastActive(); - DCHECK(browser); - if (browser->window()->IsFullscreen()) { + // browser can be NULL if we receive a lock request before the first browser + // window is shown. + if (browser && browser->window()->IsFullscreen()) { browser->ToggleFullscreenMode(); } - // TODO(oshima): Currently, PowerManager may send a lock screen event - // even if a screen is locked. Investigate & solve the issue and - // enable this again if it's possible. - // DCHECK(!screen_locker_); if (!screen_locker_) { + LOG(INFO) << "Show: Locking screen"; ScreenLocker* locker = new ScreenLocker(UserManager::Get()->logged_in_user()); locker->Init(); } else { - LOG(INFO) << "Show(): screen locker already exists. " + // PowerManager re-sends lock screen signal if it doesn't + // receive the response within timeout. Just send complete + // signal. + LOG(INFO) << "Show: locker already exists. " << "just sending completion event"; if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted(); @@ -610,7 +700,7 @@ void ScreenLocker::Show() { void ScreenLocker::Hide() { DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); DCHECK(screen_locker_); - LOG(INFO) << "Hide Screen Locker:" << screen_locker_; + LOG(INFO) << "Hide: Deleting ScreenLocker:" << screen_locker_; MessageLoopForUI::current()->DeleteSoon(FROM_HERE, screen_locker_); } @@ -618,13 +708,17 @@ void ScreenLocker::Hide() { void ScreenLocker::UnlockScreenFailed() { DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); if (screen_locker_) { + // Power manager decided no to unlock the screen even if a user + // typed in password, for example, when a user closed the lid + // immediately after typing in the password. + LOG(INFO) << "UnlockScreenFailed: re-enabling screen locker"; screen_locker_->EnableInput(); } else { // This can happen when a user requested unlock, but PowerManager // rejected because the computer is closed, then PowerManager unlocked // because it's open again and the above failure message arrives. // This'd be extremely rare, but may still happen. - LOG(INFO) << "Screen is unlocked"; + LOG(INFO) << "UnlockScreenFailed: screen is already unlocked."; } } @@ -641,12 +735,18 @@ ScreenLocker::~ScreenLocker() { ClearErrors(); if (input_event_observer_.get()) MessageLoopForUI::current()->RemoveObserver(input_event_observer_.get()); + if (locker_input_event_observer_.get()) { + lock_widget_->GetFocusManager()->UnregisterAccelerator( + views::Accelerator(app::VKEY_ESCAPE, false, false, false), this); + MessageLoopForUI::current()->RemoveObserver( + locker_input_event_observer_.get()); + } gdk_keyboard_ungrab(GDK_CURRENT_TIME); gdk_pointer_ungrab(GDK_CURRENT_TIME); DCHECK(lock_window_); - LOG(INFO) << "Closing ScreenLocker window"; + LOG(INFO) << "~ScreenLocker(): Closing ScreenLocker window"; lock_window_->Close(); // lock_widget_ will be deleted by gtk's destroy signal. screen_locker_ = NULL; @@ -664,10 +764,20 @@ void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { } void ScreenLocker::ScreenLockReady() { - DLOG(INFO) << "ScreenLockReady"; + LOG(INFO) << "ScreenLockReady: sending completed signal to power manager."; // Don't show the password field until we grab all inputs. lock_widget_->GetRootView()->SetVisible(true); - EnableInput(); + if (background_view_->ScreenSaverEnabled()) { + lock_widget_->GetFocusManager()->RegisterAccelerator( + views::Accelerator(app::VKEY_ESCAPE, false, false, false), this); + locker_input_event_observer_.reset(new LockerInputEventObserver(this)); + MessageLoopForUI::current()->AddObserver( + locker_input_event_observer_.get()); + StartScreenSaver(); + } else { + EnableInput(); + } + bool state = true; NotificationService::current()->Notify( NotificationType::SCREEN_LOCK_STATE_CHANGED, @@ -692,4 +802,36 @@ void ScreenLocker::OnWindowManagerReady() { ScreenLockReady(); } +void ScreenLocker::StopScreenSaver() { + if (background_view_->IsScreenSaverVisible()) { + LOG(INFO) << "StopScreenSaver"; + background_view_->HideScreenSaver(); + if (screen_lock_view_) { + screen_lock_view_->SetVisible(true); + screen_lock_view_->RequestFocus(); + } + EnableInput(); + } +} + +void ScreenLocker::StartScreenSaver() { + if (!background_view_->IsScreenSaverVisible()) { + LOG(INFO) << "StartScreenSaver"; + background_view_->ShowScreenSaver(); + if (screen_lock_view_) { + screen_lock_view_->SetEnabled(false); + screen_lock_view_->SetVisible(false); + } + ClearErrors(); + } +} + +bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) { + if (!background_view_->IsScreenSaverVisible()) { + StartScreenSaver(); + return true; + } + return false; +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index f2681b7..5162263 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -4,13 +4,15 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_H_ +#pragma once #include <string> #include "base/task.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/user_manager.h" -#include "chrome/browser/views/info_bubble.h" +#include "views/accelerator.h" namespace gfx { class Rect; @@ -23,10 +25,13 @@ class WidgetGtk; namespace chromeos { class Authenticator; +class BackgroundView; class InputEventObserver; +class LockerInputEventObserver; class MessageBubble; class MouseEventRelay; class ScreenLockView; +class LoginFailure; namespace test { class ScreenLockerTester; @@ -36,7 +41,8 @@ class ScreenLockerTester; // authenticate the user. ScreenLocker manages its life cycle and will // delete itself when it's unlocked. class ScreenLocker : public LoginStatusConsumer, - public InfoBubbleDelegate { + public MessageBubbleDelegate, + public views::AcceleratorTarget { public: explicit ScreenLocker(const UserManager::User& user); @@ -44,7 +50,7 @@ class ScreenLocker : public LoginStatusConsumer, void Init(); // LoginStatusConsumer implements: - virtual void OnLoginFailure(const std::string& error); + virtual void OnLoginFailure(const chromeos::LoginFailure& error); virtual void OnLoginSuccess(const std::string& username, const GaiaAuthConsumer::ClientLoginResult& result); @@ -53,6 +59,7 @@ class ScreenLocker : public LoginStatusConsumer, bool closed_by_escape); virtual bool CloseOnEscape() { return true; } virtual bool FadeInOnShow() { return false; } + virtual void OnHelpLinkActivated() {} // Authenticates the user with given |password| and authenticator. void Authenticate(const string16& password); @@ -94,6 +101,7 @@ class ScreenLocker : public LoginStatusConsumer, private: friend class DeleteTask<ScreenLocker>; friend class test::ScreenLockerTester; + friend class LockerInputEventObserver; virtual ~ScreenLocker(); @@ -106,6 +114,15 @@ class ScreenLocker : public LoginStatusConsumer, // Called when the window manager is ready to handle locked state. void OnWindowManagerReady(); + // Stops screen saver. + void StopScreenSaver(); + + // Starts screen saver. + void StartScreenSaver(); + + // Overridden from AcceleratorTarget: + virtual bool AcceleratorPressed(const views::Accelerator& accelerator); + // Event handler for client-event. CHROMEGTK_CALLBACK_1(ScreenLocker, void, OnClientEvent, GdkEventClient*); @@ -118,6 +135,9 @@ class ScreenLocker : public LoginStatusConsumer, // A view that accepts password. ScreenLockView* screen_lock_view_; + // A view that can display html page as background. + BackgroundView* background_view_; + // Logged in user. UserManager::User user_; @@ -134,6 +154,10 @@ class ScreenLocker : public LoginStatusConsumer, // Used when |unlock_on_input_| is true. scoped_ptr<InputEventObserver> input_event_observer_; + // A message loop observer to detect user's keyboard/mouse event. + // Used when to show the screen locker upon such an event. + scoped_ptr<LockerInputEventObserver> locker_input_event_observer_; + // An info bubble to display login failure message. MessageBubble* error_info_; diff --git a/chrome/browser/chromeos/login/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/screen_locker_browsertest.cc index 01b615c..2b0258b 100644 --- a/chrome/browser/chromeos/login/screen_locker_browsertest.cc +++ b/chrome/browser/chromeos/login/screen_locker_browsertest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "base/message_loop.h" #include "base/scoped_ptr.h" #include "chrome/browser/automation/ui_controls.h" @@ -9,11 +10,11 @@ #include "chrome/browser/browser_window.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" -#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h" #include "chrome/browser/chromeos/cros/mock_input_method_library.h" +#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h" +#include "chrome/browser/chromeos/login/mock_authenticator.h" #include "chrome/browser/chromeos/login/screen_locker.h" #include "chrome/browser/chromeos/login/screen_locker_tester.h" -#include "chrome/browser/chromeos/login/mock_authenticator.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/views/browser_dialogs.h" #include "chrome/common/chrome_switches.h" @@ -30,7 +31,7 @@ namespace { // An object that wait for lock state and fullscreen state. class Waiter : public NotificationObserver { public: - Waiter(Browser* browser) + explicit Waiter(Browser* browser) : browser_(browser) { registrar_.Add(this, NotificationType::SCREEN_LOCK_STATE_CHANGED, @@ -90,9 +91,13 @@ namespace chromeos { class ScreenLockerTest : public CrosInProcessBrowserTest { public: - ScreenLockerTest() {} + ScreenLockerTest() : mock_screen_lock_library_(NULL), + mock_input_method_library_(NULL) { + } protected: + MockScreenLockLibrary *mock_screen_lock_library_; + MockInputMethodLibrary *mock_input_method_library_; // Test the no password mode with different unlock scheme given by // |unlock| function. @@ -125,8 +130,10 @@ class ScreenLockerTest : public CrosInProcessBrowserTest { private: virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - InitMockScreenLockLibrary(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->InitMockScreenLockLibrary(); + mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library(); + mock_input_method_library_ = cros_mock_->mock_input_method_library(); EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_)) .Times(1) .RetiresOnSaturation(); @@ -134,13 +141,13 @@ class ScreenLockerTest : public CrosInProcessBrowserTest { .Times(1) .RetiresOnSaturation(); // Expectations for the status are on the screen lock window. - SetStatusAreaMocksExpectations(); + cros_mock_->SetStatusAreaMocksExpectations(); // Expectations for the status area on the browser window. - SetStatusAreaMocksExpectations(); + cros_mock_->SetStatusAreaMocksExpectations(); } virtual void SetUpCommandLine(CommandLine* command_line) { - command_line->AppendSwitchWithValue(switches::kLoginProfile, "user"); + command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); command_line->AppendSwitch(switches::kNoFirstRun); } @@ -165,6 +172,14 @@ IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) { ui_test_utils::WaitForNotification( NotificationType::SCREEN_LOCK_STATE_CHANGED); + // Test to make sure that the widget is actually appearing and is of + // reasonable size, preventing a regression of + // http://code.google.com/p/chromium-os/issues/detail?id=5987 + gfx::Rect lock_bounds; + tester->GetChildWidget()->GetBounds(&lock_bounds, true); + EXPECT_GT(lock_bounds.width(), 10); + EXPECT_GT(lock_bounds.height(), 10); + tester->InjectMockAuthenticator("user", "pass"); EXPECT_TRUE(tester->IsLocked()); tester->EnterPassword("fail"); @@ -208,6 +223,7 @@ IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) { } tester->InjectMockAuthenticator("user", "pass"); tester->EnterPassword("pass"); + ui_test_utils::RunAllPendingInMessageLoop(); ScreenLocker::Hide(); ui_test_utils::RunAllPendingInMessageLoop(); EXPECT_FALSE(tester->IsLocked()); @@ -231,7 +247,7 @@ IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithMouseClick) { void KeyPress(views::Widget* widget) { ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()), - base::VKEY_SPACE, false, false, false, false); + app::VKEY_SPACE, false, false, false, false); } IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithKeyPress) { diff --git a/chrome/browser/chromeos/login/screen_locker_tester.cc b/chrome/browser/chromeos/login/screen_locker_tester.cc index 8172fb9..aeec099 100644 --- a/chrome/browser/chromeos/login/screen_locker_tester.cc +++ b/chrome/browser/chromeos/login/screen_locker_tester.cc @@ -6,16 +6,16 @@ #include <gdk/gdkkeysyms.h> -#include "app/l10n_util.h" +#include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/login/mock_authenticator.h" -#include "chrome/browser/chromeos/login/screen_locker.h" #include "chrome/browser/chromeos/login/screen_lock_view.h" +#include "chrome/browser/chromeos/login/screen_locker.h" #include "views/controls/button/button.h" #include "views/controls/label.h" #include "views/controls/textfield/textfield.h" -#include "views/widget/widget_gtk.h" #include "views/widget/root_view.h" +#include "views/widget/widget_gtk.h" namespace chromeos { @@ -65,6 +65,11 @@ views::Widget* ScreenLockerTester::GetWidget() { return ScreenLocker::screen_locker_->lock_window_; } +views::Widget* ScreenLockerTester::GetChildWidget() { + DCHECK(ScreenLocker::screen_locker_); + return ScreenLocker::screen_locker_->lock_widget_; +} + } // namespace test } // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker_tester.h b/chrome/browser/chromeos/login/screen_locker_tester.h index 33dc41e..bc8800e 100644 --- a/chrome/browser/chromeos/login/screen_locker_tester.h +++ b/chrome/browser/chromeos/login/screen_locker_tester.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_TESTER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_TESTER_H_ +#pragma once #include "base/basictypes.h" @@ -38,6 +39,8 @@ class ScreenLockerTester { // Returns the widget for screen locker window. views::Widget* GetWidget(); + views::Widget* GetChildWidget(); + private: friend class chromeos::ScreenLocker; diff --git a/chrome/browser/chromeos/login/screen_observer.h b/chrome/browser/chromeos/login/screen_observer.h index 85875f1..4cba1bb 100644 --- a/chrome/browser/chromeos/login/screen_observer.h +++ b/chrome/browser/chromeos/login/screen_observer.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_OBSERVER_H_ +#pragma once #include <string> @@ -34,6 +35,7 @@ class ScreenObserver { USER_IMAGE_SELECTED, USER_IMAGE_SKIPPED, EULA_ACCEPTED, + EULA_BACK, REGISTRATION_SUCCESS, REGISTRATION_SKIPPED, EXIT_CODES_COUNT // not a real code, must be the last diff --git a/chrome/browser/chromeos/login/update_screen.cc b/chrome/browser/chromeos/login/update_screen.cc index 8e9a05a..20b04b0 100644 --- a/chrome/browser/chromeos/login/update_screen.cc +++ b/chrome/browser/chromeos/login/update_screen.cc @@ -5,18 +5,24 @@ #include "chrome/browser/chromeos/login/update_screen.h" #include "base/logging.h" +#include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/chromeos/login/update_view.h" namespace { -// Update window should appear for at least kMinimalUpdateTime seconds. -const int kMinimalUpdateTime = 3; +// Progress bar stages. Each represents progress bar value +// at the beginning of each stage. +// TODO(nkostylev): Base stage progress values on approximate time. +// TODO(nkostylev): Animate progress during each state. +const int kBeforeUpdateCheckProgress = 7; +const int kBeforeDownloadProgress = 14; +const int kBeforeVerifyingProgress = 74; +const int kBeforeFinalizingProgress = 81; +const int kProgressComplete = 100; -// Progress bar increment step. -const int kBeforeUpdateCheckProgressIncrement = 10; -const int kAfterUpdateCheckProgressIncrement = 10; -const int kUpdateCompleteProgressIncrement = 75; +// Defines what part of update progress does download part takes. +const int kDownloadProgressIncrement = 60; } // anonymous namespace @@ -24,64 +30,64 @@ namespace chromeos { UpdateScreen::UpdateScreen(WizardScreenDelegate* delegate) : DefaultViewScreen<chromeos::UpdateView>(delegate), - update_result_(UPGRADE_STARTED), - update_error_(GOOGLE_UPDATE_NO_ERROR), - checking_for_update_(true) { + checking_for_update_(true), + maximal_curtain_time_(0), + reboot_check_delay_(0) { } UpdateScreen::~UpdateScreen() { // Remove pointer to this object from view. if (view()) view()->set_controller(NULL); - // Google Updater is holding a pointer to us until it reports status, - // so we need to remove it in case we were still listening. - if (google_updater_.get()) - google_updater_->set_status_listener(NULL); + CrosLibrary::Get()->GetUpdateLibrary()->RemoveObserver(this); } -void UpdateScreen::OnReportResults(GoogleUpdateUpgradeResult result, - GoogleUpdateErrorCode error_code, - const std::wstring& version) { - // Drop the last reference to the object so that it gets cleaned up here. - if (google_updater_.get()) { - google_updater_->set_status_listener(NULL); - google_updater_ = NULL; +void UpdateScreen::UpdateStatusChanged(UpdateLibrary* library) { + UpdateStatusOperation status = library->status().status; + if (checking_for_update_ && status > UPDATE_STATUS_CHECKING_FOR_UPDATE) { + checking_for_update_ = false; } - // Depending on the result decide what to do next. - update_result_ = result; - update_error_ = error_code; - LOG(INFO) << "Update result: " << result; - if (error_code != GOOGLE_UPDATE_NO_ERROR) - LOG(INFO) << "Update error code: " << error_code; - LOG(INFO) << "Update version: " << version; - switch (update_result_) { - case UPGRADE_IS_AVAILABLE: - checking_for_update_ = false; - // Advance view progress bar. - view()->AddProgress(kAfterUpdateCheckProgressIncrement); - // Create new Google Updater instance and install the update. - google_updater_ = CreateGoogleUpdate(); - google_updater_->CheckForUpdate(true, NULL); - LOG(INFO) << "Installing an update"; + + switch (status) { + case UPDATE_STATUS_CHECKING_FOR_UPDATE: + // Do nothing in these cases, we don't want to notify the user of the + // check unless there is an update. break; - case UPGRADE_SUCCESSFUL: - view()->AddProgress(kUpdateCompleteProgressIncrement); - minimal_update_time_timer_.Stop(); - checking_for_update_ = false; - // TODO(nkostylev): Call reboot API. http://crosbug.com/4002 - ExitUpdate(); + case UPDATE_STATUS_UPDATE_AVAILABLE: + view()->SetProgress(kBeforeDownloadProgress); + LOG(INFO) << "Update available: " << library->status().new_version; break; - case UPGRADE_ALREADY_UP_TO_DATE: - checking_for_update_ = false; - view()->AddProgress(kAfterUpdateCheckProgressIncrement); - // Fall through. - case UPGRADE_ERROR: - if (MinimalUpdateTimeElapsed()) { - ExitUpdate(); + case UPDATE_STATUS_DOWNLOADING: + { + view()->ShowCurtain(false); + int download_progress = static_cast<int>( + library->status().download_progress * kDownloadProgressIncrement); + view()->SetProgress(kBeforeDownloadProgress + download_progress); } break; + case UPDATE_STATUS_VERIFYING: + view()->SetProgress(kBeforeVerifyingProgress); + break; + case UPDATE_STATUS_FINALIZING: + view()->SetProgress(kBeforeFinalizingProgress); + break; + case UPDATE_STATUS_UPDATED_NEED_REBOOT: + view()->SetProgress(kProgressComplete); + view()->ShowCurtain(false); + CrosLibrary::Get()->GetUpdateLibrary()->RebootAfterUpdate(); + LOG(INFO) << "Reboot API was called. Waiting for reboot."; + reboot_timer_.Start(base::TimeDelta::FromSeconds(reboot_check_delay_), + this, + &UpdateScreen::OnWaitForRebootTimeElapsed); + break; + case UPDATE_STATUS_IDLE: + case UPDATE_STATUS_ERROR: + case UPDATE_STATUS_REPORTING_ERROR_EVENT: + ExitUpdate(); + break; default: NOTREACHED(); + break; } } @@ -90,72 +96,81 @@ void UpdateScreen::StartUpdate() { view()->Reset(); view()->set_controller(this); - // Start the minimal update time timer. - minimal_update_time_timer_.Start( - base::TimeDelta::FromSeconds(kMinimalUpdateTime), - this, - &UpdateScreen::OnMinimalUpdateTimeElapsed); - - // Create Google Updater object and check if there is an update available. - checking_for_update_ = true; - google_updater_ = CreateGoogleUpdate(); - google_updater_->CheckForUpdate(false, NULL); - view()->AddProgress(kBeforeUpdateCheckProgressIncrement); - LOG(INFO) << "Checking for update"; + // Start the maximal curtain time timer. + if (maximal_curtain_time_ > 0) { + maximal_curtain_time_timer_.Start( + base::TimeDelta::FromSeconds(maximal_curtain_time_), + this, + &UpdateScreen::OnMaximalCurtainTimeElapsed); + } else { + view()->ShowCurtain(false); + } + + view()->SetProgress(kBeforeUpdateCheckProgress); + + if (!CrosLibrary::Get()->EnsureLoaded()) { + LOG(ERROR) << "Error loading CrosLibrary"; + } else { + CrosLibrary::Get()->GetUpdateLibrary()->AddObserver(this); + LOG(INFO) << "Checking for update"; + if (!CrosLibrary::Get()->GetUpdateLibrary()->CheckForUpdate()) { + ExitUpdate(); + } + } } void UpdateScreen::CancelUpdate() { #if !defined(OFFICIAL_BUILD) - update_result_ = UPGRADE_ALREADY_UP_TO_DATE; - update_error_ = GOOGLE_UPDATE_NO_ERROR; ExitUpdate(); #endif } void UpdateScreen::ExitUpdate() { - if (google_updater_.get()) { - google_updater_->set_status_listener(NULL); - google_updater_ = NULL; - } - minimal_update_time_timer_.Stop(); + maximal_curtain_time_timer_.Stop(); ScreenObserver* observer = delegate()->GetObserver(this); - if (observer) { - switch (update_result_) { - case UPGRADE_ALREADY_UP_TO_DATE: - observer->OnExit(ScreenObserver::UPDATE_NOUPDATE); - break; - case UPGRADE_SUCCESSFUL: - observer->OnExit(ScreenObserver::UPDATE_INSTALLED); - break; - case UPGRADE_ERROR: - if (checking_for_update_) { - observer->OnExit(ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE); - } else { - observer->OnExit(ScreenObserver::UPDATE_ERROR_UPDATING); - } - break; - default: - NOTREACHED(); - } + + if (!CrosLibrary::Get()->EnsureLoaded()) { + observer->OnExit(ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE); + } + + UpdateLibrary* update_library = CrosLibrary::Get()->GetUpdateLibrary(); + update_library->RemoveObserver(this); + switch (update_library->status().status) { + case UPDATE_STATUS_IDLE: + observer->OnExit(ScreenObserver::UPDATE_NOUPDATE); + break; + case UPDATE_STATUS_ERROR: + case UPDATE_STATUS_REPORTING_ERROR_EVENT: + observer->OnExit(checking_for_update_ ? + ScreenObserver::UPDATE_ERROR_CHECKING_FOR_UPDATE : + ScreenObserver::UPDATE_ERROR_UPDATING); + break; + default: + NOTREACHED(); } } -bool UpdateScreen::MinimalUpdateTimeElapsed() { - return !minimal_update_time_timer_.IsRunning(); +void UpdateScreen::OnMaximalCurtainTimeElapsed() { + view()->ShowCurtain(false); } -GoogleUpdate* UpdateScreen::CreateGoogleUpdate() { - GoogleUpdate* updater = new GoogleUpdate(); - updater->set_status_listener(this); - return updater; +void UpdateScreen::OnWaitForRebootTimeElapsed() { + LOG(ERROR) << "Unable to reboot - asking user for a manual reboot."; + view()->ShowManualRebootInfo(); } -void UpdateScreen::OnMinimalUpdateTimeElapsed() { - if (update_result_ == UPGRADE_SUCCESSFUL || - update_result_ == UPGRADE_ALREADY_UP_TO_DATE || - update_result_ == UPGRADE_ERROR) { - ExitUpdate(); - } +void UpdateScreen::SetMaximalCurtainTime(int seconds) { + if (seconds <= 0) + maximal_curtain_time_timer_.Stop(); + DCHECK(!maximal_curtain_time_timer_.IsRunning()); + maximal_curtain_time_ = seconds; +} + +void UpdateScreen::SetRebootCheckDelay(int seconds) { + if (seconds <= 0) + reboot_timer_.Stop(); + DCHECK(!reboot_timer_.IsRunning()); + reboot_check_delay_ = seconds; } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/update_screen.h b/chrome/browser/chromeos/login/update_screen.h index 825a2b5..2253c2e 100644 --- a/chrome/browser/chromeos/login/update_screen.h +++ b/chrome/browser/chromeos/login/update_screen.h @@ -4,12 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UPDATE_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_UPDATE_SCREEN_H_ +#pragma once -#include "base/ref_counted.h" #include "base/timer.h" +#include "chrome/browser/chromeos/cros/update_library.h" #include "chrome/browser/chromeos/login/update_view.h" #include "chrome/browser/chromeos/login/view_screen.h" -#include "chrome/browser/google_update.h" namespace chromeos { @@ -22,16 +22,14 @@ class UpdateController { }; class UpdateScreen: public DefaultViewScreen<chromeos::UpdateView>, - public GoogleUpdateStatusListener, + public UpdateLibrary::Observer, public UpdateController { public: explicit UpdateScreen(WizardScreenDelegate* delegate); virtual ~UpdateScreen(); - // Overridden from GoogleUpdateStatusListener: - virtual void OnReportResults(GoogleUpdateUpgradeResult result, - GoogleUpdateErrorCode error_code, - const std::wstring& version); + // UpdateLibrary::Observer implementation: + virtual void UpdateStatusChanged(UpdateLibrary* library); // Overridden from UpdateController: virtual void StartUpdate(); @@ -39,25 +37,38 @@ class UpdateScreen: public DefaultViewScreen<chromeos::UpdateView>, // Reports update results to the ScreenObserver. virtual void ExitUpdate(); - // Returns true if minimal update time has elapsed. - virtual bool MinimalUpdateTimeElapsed(); - // Creates GoogleUpdate object. - virtual GoogleUpdate* CreateGoogleUpdate(); + + // Maximal curtain time get/set, in seconds. + int maximal_curtain_time() const { return maximal_curtain_time_; } + void SetMaximalCurtainTime(int seconds); + + // Reboot check delay get/set, in seconds. + int reboot_check_delay() const { return reboot_check_delay_; } + void SetRebootCheckDelay(int seconds); private: - // Timer notification handler. - void OnMinimalUpdateTimeElapsed(); + // Timer notification handlers. + void OnMaximalCurtainTimeElapsed(); + void OnWaitForRebootTimeElapsed(); - // Timer. - base::OneShotTimer<UpdateScreen> minimal_update_time_timer_; + // Timer for the interval while curtain view is shown. + base::OneShotTimer<UpdateScreen> maximal_curtain_time_timer_; - // Update status. - GoogleUpdateUpgradeResult update_result_; - GoogleUpdateErrorCode update_error_; + // Timer for the interval to wait for the reboot. + // If reboot didn't happen - ask user to reboot manually. + base::OneShotTimer<UpdateScreen> reboot_timer_; + + // True if in the process of checking for update. bool checking_for_update_; - // Google Updater. - scoped_refptr<GoogleUpdate> google_updater_; + // Maximal time (secs) the curtain view is shown. Updating progress is shown + // to the user when this time elapsed. + int maximal_curtain_time_; + + // Time in seconds after which we decide that the device has not rebooted + // automatically. If reboot didn't happen during this interval, ask user to + // reboot device manually. + int reboot_check_delay_; DISALLOW_COPY_AND_ASSIGN(UpdateScreen); }; diff --git a/chrome/browser/chromeos/login/update_view.cc b/chrome/browser/chromeos/login/update_view.cc index 9facdbe..59e20dd 100644 --- a/chrome/browser/chromeos/login/update_view.cc +++ b/chrome/browser/chromeos/login/update_view.cc @@ -8,13 +8,16 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/update_screen.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "views/border.h" #include "views/controls/label.h" #include "views/controls/progress_bar.h" +#include "views/controls/throbber.h" #include "views/focus/focus_manager.h" #include "views/widget/widget.h" @@ -25,29 +28,42 @@ using views::Widget; namespace { +// TODO(nkostylev): Switch to GridLayout. + // Y offset for the 'installing updates' label. -const int kInstallingUpdatesLabelY = 200; +const int kInstallingUpdatesLabelYBottomFromProgressBar = 18; // Y offset for the progress bar. -const int kProgressBarY = 250; +const int kProgressBarY = 199; +// Y offset for the 'computer will restart' label. +const int kRebootLabelYFromProgressBar = 77; // Y offset for the 'ESCAPE to skip' label. -const int kEscapeToSkipLabelY = 290; +const int kEscapeToSkipLabelY = 48; // Progress bar width. -const int kProgressBarWidth = 450; +const int kProgressBarWidth = 550; +// Progress bar height. +const int kProgressBarHeight = 18; // Horizontal spacing (ex. min left and right margins for label on the screen). -const int kHorizontalSpacing = 25; +const int kHorizontalSpacing = 75; +// Horizontal spacing between spinner and label on the curtain screen. +const int kBetweenSpacing = 25; // Label color. const SkColor kLabelColor = 0xFF000000; const SkColor kSkipLabelColor = 0xFFAA0000; +const SkColor kManualRebootLabelColor = 0xFFAA0000; } // namespace namespace chromeos { UpdateView::UpdateView(chromeos::ScreenObserver* observer) - : escape_accelerator_(base::VKEY_ESCAPE, false, false, false), + : escape_accelerator_(app::VKEY_ESCAPE, false, false, false), installing_updates_label_(NULL), + reboot_label_(NULL), + manual_reboot_label_(NULL), progress_bar_(NULL), + show_curtain_(true), + show_manual_reboot_label_(false), observer_(observer) { } @@ -60,30 +76,35 @@ void UpdateView::Init() { &chromeos::BorderDefinition::kScreenBorder); set_background(views::Background::CreateBackgroundPainter(true, painter)); - ResourceBundle& res_bundle = ResourceBundle::GetSharedInstance(); - gfx::Font base_font = res_bundle.GetFont(ResourceBundle::BaseFont); - InitLabel(&installing_updates_label_); - installing_updates_label_->SetFont(base_font); + InitLabel(&reboot_label_); + InitLabel(&manual_reboot_label_); + manual_reboot_label_->SetVisible(false); + manual_reboot_label_->SetColor(kManualRebootLabelColor); progress_bar_ = new views::ProgressBar(); AddChildView(progress_bar_); - UpdateLocalizedStrings(); + // Curtain view. + InitLabel(&checking_label_); + throbber_ = CreateDefaultThrobber(); + AddChildView(throbber_); #if !defined(OFFICIAL_BUILD) - escape_to_skip_label_ = new views::Label(); + InitLabel(&escape_to_skip_label_); escape_to_skip_label_->SetColor(kSkipLabelColor); - escape_to_skip_label_->SetFont(base_font); escape_to_skip_label_->SetText( L"Press ESCAPE to skip (Non-official builds only)"); - AddChildView(escape_to_skip_label_); #endif + + UpdateLocalizedStrings(); + UpdateVisibility(); } void UpdateView::Reset() { progress_bar_->SetProgress(0); #if !defined(OFFICIAL_BUILD) + ResetAccelerators(); AddAccelerator(escape_accelerator_); #endif } @@ -92,35 +113,86 @@ void UpdateView::UpdateLocalizedStrings() { installing_updates_label_->SetText( l10n_util::GetStringF(IDS_INSTALLING_UPDATE, l10n_util::GetString(IDS_PRODUCT_OS_NAME))); + reboot_label_->SetText(l10n_util::GetString(IDS_INSTALLING_UPDATE_DESC)); + manual_reboot_label_->SetText(l10n_util::GetString(IDS_UPDATE_COMPLETED)); + checking_label_->SetText(l10n_util::GetString(IDS_CHECKING_FOR_UPDATES)); } void UpdateView::AddProgress(int ticks) { progress_bar_->AddProgress(ticks); } -// Sets the bounds of the view, placing it at the center of the screen -// with the |y| coordinate provided. |width| could specify desired view width -// otherwise preferred width/height are used. -// |x_center| specifies screen center. -static void setViewBounds( - views::View* view, int x_center, int y, int width = -1) { - int preferred_width = (width >= 0) ? width : view->GetPreferredSize().width(); +void UpdateView::SetProgress(int progress) { + progress_bar_->SetProgress(progress); +} + +void UpdateView::ShowManualRebootInfo() { + show_manual_reboot_label_ = true; + UpdateVisibility(); +} + +void UpdateView::ShowCurtain(bool show_curtain) { + if (show_curtain_ != show_curtain) { + show_curtain_ = show_curtain; + UpdateVisibility(); + } +} + +// Sets the bounds of the view, placing center of the view at the given +// coordinates (|x| and |y|). +static void setViewBounds(views::View* view, int x, int y) { + int preferred_width = view->GetPreferredSize().width(); int preferred_height = view->GetPreferredSize().height(); view->SetBounds( - x_center - preferred_width / 2, - y, + x - preferred_width / 2, + y - preferred_height / 2, preferred_width, preferred_height); } void UpdateView::Layout() { - int x_center = width() / 2; int max_width = width() - GetInsets().width() - 2 * kHorizontalSpacing; + int right_margin = GetInsets().right() + kHorizontalSpacing; + int max_height = height() - GetInsets().height(); + int vertical_center = GetInsets().top() + max_height / 2; + installing_updates_label_->SizeToFit(max_width); - setViewBounds(installing_updates_label_, x_center, kInstallingUpdatesLabelY); - setViewBounds(progress_bar_, x_center, kProgressBarY, kProgressBarWidth); + reboot_label_->SizeToFit(max_width); + manual_reboot_label_->SizeToFit(max_width); + + progress_bar_->SetBounds(right_margin, + vertical_center - kProgressBarHeight / 2, + max_width, + kProgressBarHeight); + + installing_updates_label_->SetX(right_margin); + installing_updates_label_->SetY( + progress_bar_->y() - + kInstallingUpdatesLabelYBottomFromProgressBar - + installing_updates_label_->height()); + reboot_label_->SetX(right_margin); + reboot_label_->SetY( + progress_bar_->y() + + progress_bar_->height() + + kRebootLabelYFromProgressBar); + manual_reboot_label_->SetX(reboot_label_->x()); + manual_reboot_label_->SetY(reboot_label_->y()); + // Curtain layout is independed. + int x_center = width() / 2; + int throbber_width = throbber_->GetPreferredSize().width(); + checking_label_->SizeToFit(max_width - throbber_width - kBetweenSpacing); + int checking_label_width = checking_label_->GetPreferredSize().width(); + int space_half = (kBetweenSpacing + 1) / 2; + setViewBounds( + throbber_, x_center - checking_label_width / 2 - space_half, + vertical_center); + setViewBounds( + checking_label_, x_center + (throbber_width + 1) / 2 + space_half, + vertical_center); #if !defined(OFFICIAL_BUILD) - setViewBounds(escape_to_skip_label_, x_center, kEscapeToSkipLabelY); + escape_to_skip_label_->SizeToFit(max_width); + escape_to_skip_label_->SetX(right_margin); + escape_to_skip_label_->SetY(kEscapeToSkipLabelY); #endif SchedulePaint(); } @@ -128,7 +200,7 @@ void UpdateView::Layout() { bool UpdateView::AcceleratorPressed(const views::Accelerator& a) { #if !defined(OFFICIAL_BUILD) RemoveAccelerator(escape_accelerator_); - if (a.GetKeyCode() == base::VKEY_ESCAPE) { + if (a.GetKeyCode() == app::VKEY_ESCAPE) { if (controller_ != NULL) { controller_->CancelUpdate(); } @@ -138,12 +210,38 @@ bool UpdateView::AcceleratorPressed(const views::Accelerator& a) { return false; } +void UpdateView::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (is_add && this == child) + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); +} + void UpdateView::InitLabel(views::Label** label) { *label = new views::Label(); (*label)->SetColor(kLabelColor); - (*label)->SetHorizontalAlignment(views::Label::ALIGN_CENTER); + (*label)->SetHorizontalAlignment(views::Label::ALIGN_LEFT); (*label)->SetMultiLine(true); + + ResourceBundle& res_bundle = ResourceBundle::GetSharedInstance(); + gfx::Font label_font = res_bundle.GetFont(ResourceBundle::MediumBoldFont); + (*label)->SetFont(label_font); + AddChildView(*label); } +void UpdateView::UpdateVisibility() { + installing_updates_label_->SetVisible( + !show_curtain_&& !show_manual_reboot_label_); + reboot_label_->SetVisible(!show_curtain_&& !show_manual_reboot_label_); + manual_reboot_label_->SetVisible(!show_curtain_ && show_manual_reboot_label_); + progress_bar_->SetVisible(!show_curtain_); + + checking_label_->SetVisible(show_curtain_); + throbber_->SetVisible(show_curtain_); + if (show_curtain_) { + throbber_->Start(); + } else { + throbber_->Stop(); + } +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/update_view.h b/chrome/browser/chromeos/login/update_view.h index b5b09fb..0549edf 100644 --- a/chrome/browser/chromeos/login/update_view.h +++ b/chrome/browser/chromeos/login/update_view.h @@ -4,12 +4,14 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UPDATE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_UPDATE_VIEW_H_ +#pragma once #include "views/view.h" namespace views { class Label; class ProgressBar; +class Throbber; } // namespace views namespace chromeos { @@ -23,34 +25,66 @@ class UpdateView : public views::View { explicit UpdateView(ScreenObserver* observer); virtual ~UpdateView(); - virtual void Init(); - virtual void Reset(); - virtual void UpdateLocalizedStrings(); + void Init(); + void Reset(); + void UpdateLocalizedStrings(); // Sets update controller. - virtual void set_controller(UpdateController* controller) { + void set_controller(UpdateController* controller) { controller_ = controller; } // Advances view's progress bar. Maximum progress is 100. - virtual void AddProgress(int progress); + void AddProgress(int progress); + + // Sets the current value for the progress bar. Maximum progress is 100. + void SetProgress(int progress); + + // Shows label with instructions for user to do a manual reboot. + // Usually is not called since we rely on API that will reboot after update. + void ShowManualRebootInfo(); + + // Whether curtain window with throbber and label in the center should + // be shown. + void ShowCurtain(bool show_curtain); // views::View implementation: virtual void Layout(); virtual bool AcceleratorPressed(const views::Accelerator& a); + protected: + // views::View implementation: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + private: // Creates Label control and adds it as a child. void InitLabel(views::Label** label); + // Updates visibility of the elements. + void UpdateVisibility(); + // Keyboard accelerator to allow cancelling update by hitting escape. views::Accelerator escape_accelerator_; // Dialog controls. views::Label* installing_updates_label_; + views::Label* reboot_label_; + views::Label* manual_reboot_label_; views::Label* escape_to_skip_label_; views::ProgressBar* progress_bar_; + // Curtain views. + views::Label* checking_label_; + views::Throbber* throbber_; + + // Show curtain view? + bool show_curtain_; + + // Show manual reboot label? + bool show_manual_reboot_label_; + // Notifications receiver. chromeos::ScreenObserver* observer_; // Update controller. diff --git a/chrome/browser/chromeos/login/user_controller.cc b/chrome/browser/chromeos/login/user_controller.cc index e7de077..c005270 100644 --- a/chrome/browser/chromeos/login/user_controller.cc +++ b/chrome/browser/chromeos/login/user_controller.cc @@ -9,20 +9,24 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/login/existing_user_view.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/user_view.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" +#include "cros/chromeos_wm_ipc_enums.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "cros/chromeos_wm_ipc_enums.h" #include "views/background.h" -#include "views/controls/label.h" #include "views/controls/button/native_button.h" +#include "views/controls/label.h" #include "views/grid_layout.h" +#include "views/screen.h" #include "views/widget/root_view.h" #include "views/widget/widget_gtk.h" @@ -38,7 +42,7 @@ namespace { const int kUserNameGap = 4; // Approximate height of controls window, this constant is used in new user -// case to make border window size close to exsisting users. +// case to make border window size close to existing users. const int kControlsHeight = 26; // Widget that notifies window manager about clicking on itself. @@ -54,7 +58,7 @@ class ClickNotifyingWidget : public views::WidgetGtk { private: gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) { if (!controller_->is_user_selected()) - controller_->SelectUser(controller_->user_index()); + controller_->SelectUser(controller_->user_index(), true); return views::WidgetGtk::OnButtonPress(widget, event); } @@ -64,81 +68,6 @@ class ClickNotifyingWidget : public views::WidgetGtk { DISALLOW_COPY_AND_ASSIGN(ClickNotifyingWidget); }; -// Returns tooltip text for user name. Tooltip contains user's display name -// and his email domain to distinguish this user from the other one with the -// same display name. -std::string GetNameTooltip(const UserManager::User& user) { - const std::string& email = user.email(); - size_t at_pos = email.rfind('@'); - if (at_pos == std::string::npos) { - NOTREACHED(); - return std::string(); - } - size_t domain_start = at_pos + 1; - std::string domain = email.substr(domain_start, - email.length() - domain_start); - return StringPrintf("%s (%s)", - user.GetDisplayName().c_str(), - domain.c_str()); -} - -// NativeButton that will always return focus to password field. -class UserEntryNativeButton : public views::NativeButton { - public: - UserEntryNativeButton(UserController* controller, - views::ButtonListener* listener, - const std::wstring& label) - : NativeButton(listener, label), - controller_(controller) {} - - // Overridden from View: - virtual void DidGainFocus() { - controller_->FocusPasswordField(); - } - - private: - UserController* controller_; - - DISALLOW_COPY_AND_ASSIGN(UserEntryNativeButton); -}; - -// Textfield with custom processing for Tab/Shift+Tab to select entries. -class UserEntryTextfield : public views::Textfield { - public: - explicit UserEntryTextfield(UserController* controller) - : Textfield(), - controller_(controller) {} - - UserEntryTextfield(UserController* controller, - views::Textfield::StyleFlags style) - : Textfield(style), - controller_(controller) {} - - // Overridden from View: - virtual bool OnKeyPressed(const views::KeyEvent& e) { - if (e.GetKeyCode() == base::VKEY_TAB) { - int index = controller_->user_index() + (e.IsShiftDown() ? -1 : 1); - controller_->SelectUser(index); - return true; - } else { - return false; - } - } - - // Overridden from views::Textfield: - virtual bool SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { - if (e.GetKeyCode() == base::VKEY_TAB) - return true; - else - return views::Textfield::SkipDefaultKeyEventProcessing(e); - } - - private: - UserController* controller_; - - DISALLOW_COPY_AND_ASSIGN(UserEntryTextfield); -}; - } // namespace using login::kBackgroundColor; @@ -152,14 +81,13 @@ const int UserController::kPadding = 20; // Max size needed when an entry is not selected. const int UserController::kUnselectedSize = 100; -UserController::UserController(Delegate* delegate) +UserController::UserController(Delegate* delegate, bool is_bwsi) : user_index_(-1), is_user_selected_(false), - is_guest_(true), + is_new_user_(!is_bwsi), + is_bwsi_(is_bwsi), show_name_tooltip_(false), delegate_(delegate), - password_field_(NULL), - submit_button_(NULL), controls_window_(NULL), image_window_(NULL), border_window_(NULL), @@ -167,6 +95,7 @@ UserController::UserController(Delegate* delegate) unselected_label_window_(NULL), user_view_(NULL), new_user_view_(NULL), + existing_user_view_(NULL), label_view_(NULL), unselected_label_view_(NULL) { registrar_.Add( @@ -179,12 +108,11 @@ UserController::UserController(Delegate* delegate, const UserManager::User& user) : user_index_(-1), is_user_selected_(false), - is_guest_(false), + is_new_user_(false), + is_bwsi_(false), show_name_tooltip_(false), user_(user), delegate_(delegate), - password_field_(NULL), - submit_button_(NULL), controls_window_(NULL), image_window_(NULL), border_window_(NULL), @@ -192,6 +120,7 @@ UserController::UserController(Delegate* delegate, unselected_label_window_(NULL), user_view_(NULL), new_user_view_(NULL), + existing_user_view_(NULL), label_view_(NULL), unselected_label_view_(NULL) { registrar_.Add( @@ -208,9 +137,12 @@ UserController::~UserController() { unselected_label_window_->Close(); } -void UserController::Init(int index, int total_user_count) { +void UserController::Init(int index, + int total_user_count, + bool need_browse_without_signin) { int controls_height = 0; - controls_window_ = CreateControlsWindow(index, &controls_height); + controls_window_ = + CreateControlsWindow(index, &controls_height, need_browse_without_signin); image_window_ = CreateImageWindow(index); CreateBorderWindow(index, total_user_count, controls_height); label_window_ = CreateLabelWindow(index, WM_IPC_WINDOW_LOGIN_LABEL); @@ -219,29 +151,63 @@ void UserController::Init(int index, int total_user_count) { } void UserController::SetPasswordEnabled(bool enable) { - DCHECK(!is_guest_); - password_field_->SetEnabled(enable); - submit_button_->SetEnabled(enable); + DCHECK(!is_new_user_); + existing_user_view_->password_field()->SetEnabled(enable); + existing_user_view_->submit_button()->SetEnabled(enable); enable ? user_view_->StopThrobber() : user_view_->StartThrobber(); } +std::wstring UserController::GetNameTooltip() const { + if (is_new_user_) + return l10n_util::GetString(IDS_ADD_USER); + if (is_bwsi_) + return l10n_util::GetString(IDS_GO_INCOGNITO_BUTTON); + + // Tooltip contains user's display name and his email domain to distinguish + // this user from the other one with the same display name. + const std::wstring& email = UTF8ToWide(user_.email()); + size_t at_pos = email.rfind('@'); + if (at_pos == std::wstring::npos) { + NOTREACHED(); + return std::wstring(); + } + size_t domain_start = at_pos + 1; + std::wstring domain = email.substr(domain_start, + email.length() - domain_start); + return base::StringPrintf(L"%s (%s)", + user_.GetDisplayName().c_str(), + domain.c_str()); +} + +void UserController::UpdateSubmitButtonState() { + if (!is_new_user_) { + existing_user_view_->submit_button()->SetEnabled( + !existing_user_view_->password_field()->text().empty()); + } +} + void UserController::ClearAndEnablePassword() { - if (is_guest_) { + if (is_new_user_) { new_user_view_->ClearAndEnablePassword(); } else { - password_field_->SetText(string16()); + existing_user_view_->password_field()->SetText(string16()); SetPasswordEnabled(true); FocusPasswordField(); } } -void UserController::EnableNameTooltip(bool enable) { - if (is_guest_) - return; +void UserController::ClearAndEnableFields() { + if (is_new_user_) { + new_user_view_->ClearAndEnableFields(); + } else { + ClearAndEnablePassword(); + } +} +void UserController::EnableNameTooltip(bool enable) { std::wstring tooltip_text; if (enable) - tooltip_text = UTF8ToWide(GetNameTooltip(user_)); + tooltip_text = GetNameTooltip(); if (user_view_) user_view_->SetTooltipText(tooltip_text); @@ -259,20 +225,25 @@ void UserController::ButtonPressed(views::Button* sender, bool UserController::HandleKeystroke( views::Textfield* sender, const views::Textfield::Keystroke& keystroke) { - if (keystroke.GetKeyboardCode() == base::VKEY_RETURN) { + if (keystroke.GetKeyboardCode() == app::VKEY_RETURN) { Login(); return true; - } else if (keystroke.GetKeyboardCode() == base::VKEY_LEFT) { - SelectUser(user_index() - 1); + } else if (keystroke.GetKeyboardCode() == app::VKEY_LEFT) { + SelectUser(user_index() - 1, false); return true; - } else if (keystroke.GetKeyboardCode() == base::VKEY_RIGHT) { - SelectUser(user_index() + 1); + } else if (keystroke.GetKeyboardCode() == app::VKEY_RIGHT) { + SelectUser(user_index() + 1, false); return true; } delegate_->ClearErrors(); return false; } +void UserController::ContentsChanged(views::Textfield* sender, + const string16& new_contents) { + UpdateSubmitButtonState(); +} + void UserController::Observe( NotificationType type, const NotificationSource& source, @@ -290,20 +261,44 @@ void UserController::Observe( } void UserController::Login() { - // Delegate will reenable as necessary. - SetPasswordEnabled(false); + if (is_bwsi_) { + delegate_->LoginOffTheRecord(); + } else { + // Delegate will reenable as necessary. + SetPasswordEnabled(false); - delegate_->Login(this, password_field_->text()); + delegate_->Login(this, existing_user_view_->password_field()->text()); + } } void UserController::IsActiveChanged(bool active) { is_user_selected_ = active; if (active) { delegate_->OnUserSelected(this); - user_view_->SetMenuVisible(!is_guest_); + user_view_->SetRemoveButtonVisible(!is_new_user_ && !is_bwsi_); + // Background is NULL for inactive new user pod to make it transparent. + if (is_new_user_ && !border_window_->GetRootView()->background()) { + views::Painter* painter = CreateWizardPainter( + &BorderDefinition::kUserBorder); + border_window_->GetRootView()->set_background( + views::Background::CreateBackgroundPainter(true, painter)); + border_window_->GetRootView()->SchedulePaint(); + } } else { - user_view_->SetMenuVisible(false); + user_view_->SetRemoveButtonVisible(false); delegate_->ClearErrors(); + if (is_new_user_) { + gfx::Rect controls_bounds; + controls_window_->GetBounds(&controls_bounds, true); + gfx::Rect screen_bounds = + views::Screen::GetMonitorWorkAreaNearestWindow(NULL); + // The windows was moved out of screen so the pod was really deactivated, + // otherwise it just some dialog was shown and took focus. + if (!screen_bounds.Intersects(controls_bounds)) { + border_window_->GetRootView()->set_background(NULL); + border_window_->GetRootView()->SchedulePaint(); + } + } } } @@ -330,39 +325,27 @@ void UserController::ConfigureLoginWindow(WidgetGtk* window, window->Show(); } -WidgetGtk* UserController::CreateControlsWindow(int index, int* height) { +WidgetGtk* UserController::CreateControlsWindow( + int index, + int* height, + bool need_browse_without_signin) { views::View* control_view; - if (is_guest_) { - new_user_view_ = new NewUserView(this, false); + if (is_new_user_) { + new_user_view_ = + new NewUserView(this, false, need_browse_without_signin); new_user_view_->Init(); control_view = new_user_view_; } else { - password_field_ = new UserEntryTextfield(this, - views::Textfield::STYLE_PASSWORD); - password_field_->set_text_to_display_when_empty( - l10n_util::GetStringUTF16(IDS_LOGIN_EMPTY_PASSWORD_TEXT)); - password_field_->SetController(this); - submit_button_ = new UserEntryNativeButton( - this, this, l10n_util::GetString(IDS_LOGIN_BUTTON)); - submit_button_->SetFocusable(false); - control_view = new views::View(); - GridLayout* layout = new GridLayout(control_view); - control_view->SetLayoutManager(layout); - views::ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - column_set->AddPaddingColumn(0, kBorderSize); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, - GridLayout::USE_PREF, 0, 0); - - layout->StartRow(0, 0); - layout->AddView(password_field_); - layout->AddView(submit_button_); + existing_user_view_ = new ExistingUserView(this); + existing_user_view_->RecreateFields(); + control_view = existing_user_view_; } *height = kControlsHeight; - if (is_guest_) + if (is_new_user_) *height += kUserImageSize + kUserNameGap; + if (is_bwsi_) + *height = 1; WidgetGtk* window = new WidgetGtk(WidgetGtk::TYPE_WINDOW); ConfigureLoginWindow(window, @@ -374,13 +357,16 @@ WidgetGtk* UserController::CreateControlsWindow(int index, int* height) { } WidgetGtk* UserController::CreateImageWindow(int index) { - user_view_ = new UserView(this, true); + user_view_ = new UserView(this, true, !is_new_user_); - if (!is_guest_) { - user_view_->SetImage(user_.image()); - } else { + if (is_bwsi_) { + user_view_->SetImage(*ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_LOGIN_GUEST)); + } else if (is_new_user_) { user_view_->SetImage(*ResourceBundle::GetSharedInstance().GetBitmapNamed( - IDR_LOGIN_OTHER_USER)); + IDR_LOGIN_ADD_USER)); + } else { + user_view_->SetImage(user_.image()); } WidgetGtk* window = new ClickNotifyingWidget(WidgetGtk::TYPE_WINDOW, this); @@ -389,22 +375,30 @@ WidgetGtk* UserController::CreateImageWindow(int index) { gfx::Rect(user_view_->GetPreferredSize()), WM_IPC_WINDOW_LOGIN_IMAGE, user_view_); + return window; } void UserController::CreateBorderWindow(int index, int total_user_count, int controls_height) { - // Guest login controls window is much higher than exsisting user's controls + // New user login controls window is much higher than existing user's controls // window so window manager will place the control instead of image window. - int width = kUserImageSize + kBorderSize * 2; + int width = kBorderSize * 2 + kUserImageSize; int height = kBorderSize * 2 + controls_height; - if (!is_guest_) + if (!is_new_user_) height += kBorderSize + kUserImageSize; + if (is_bwsi_) + height = kBorderSize * 2 + kUserImageSize + 1; border_window_ = new WidgetGtk(WidgetGtk::TYPE_WINDOW); + border_window_->MakeTransparent(); border_window_->Init(NULL, gfx::Rect(0, 0, width, height)); - border_window_->GetRootView()->set_background( - views::Background::CreateSolidBackground(kBackgroundColor)); + if (!is_new_user_) { + views::Painter* painter = CreateWizardPainter( + &BorderDefinition::kUserBorder); + border_window_->GetRootView()->set_background( + views::Background::CreateBackgroundPainter(true, painter)); + } UpdateUserCount(index, total_user_count); GdkWindow* gdk_window = border_window_->GetNativeView()->window; @@ -432,8 +426,18 @@ WidgetGtk* UserController::CreateLabelWindow(int index, const gfx::Font& font = (type == WM_IPC_WINDOW_LOGIN_LABEL) ? rb.GetFont(ResourceBundle::LargeFont).DeriveFont(0, gfx::Font::BOLD) : rb.GetFont(ResourceBundle::BaseFont).DeriveFont(0, gfx::Font::BOLD); - std::wstring text = is_guest_ ? l10n_util::GetString(IDS_GUEST) : - UTF8ToWide(user_.GetDisplayName()); + std::wstring text; + if (is_bwsi_) { + text = l10n_util::GetString(IDS_GUEST); + } else if (is_new_user_) { + // Add user should have label only in activated state. + // When new user is the only, label is not needed. + if (type == WM_IPC_WINDOW_LOGIN_LABEL && index != 0) + text = l10n_util::GetString(IDS_ADD_USER); + } else { + text = UTF8ToWide(user_.GetDisplayName()); + } + views::Label* label = new views::Label(text); label->SetColor(kTextColor); label->SetFont(font); @@ -455,15 +459,10 @@ WidgetGtk* UserController::CreateLabelWindow(int index, } gfx::Rect UserController::GetScreenBounds() const { - if (is_guest_) { - return new_user_view_->GetPasswordBounds(); - } else { - gfx::Rect screen_bounds(password_field_->bounds()); - gfx::Point origin(screen_bounds.origin()); - views::View::ConvertPointToScreen(password_field_->GetParent(), &origin); - screen_bounds.set_origin(origin); - return screen_bounds; - } + if (is_new_user_) + return new_user_view_->GetUsernameBounds(); + else + return existing_user_view_->password_field()->GetScreenBounds(); } void UserController::OnLogin(const std::string& username, @@ -484,20 +483,23 @@ void UserController::ClearErrors() { delegate_->ClearErrors(); } -void UserController::OnRemoveUser() { - delegate_->RemoveUser(this); +void UserController::NavigateAway() { + SelectUser(user_index() - 1, false); } -void UserController::OnChangePhoto() { - // TODO(dpolukhin): implement change user photo, see http://crosbug.com/2348 +void UserController::OnRemoveUser() { + delegate_->RemoveUser(this); } -void UserController::SelectUser(int index) { - delegate_->SelectUser(index); +void UserController::SelectUser(int index, bool is_click) { + if (is_click && is_bwsi_) + delegate_->LoginOffTheRecord(); + else + delegate_->SelectUser(index); } void UserController::FocusPasswordField() { - password_field_->RequestFocus(); + existing_user_view_->FocusPasswordField(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_controller.h b/chrome/browser/chromeos/login/user_controller.h index d702fcd..c62e94c 100644 --- a/chrome/browser/chromeos/login/user_controller.h +++ b/chrome/browser/chromeos/login/user_controller.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_CONTROLLER_H_ +#pragma once #include <string> @@ -26,6 +27,7 @@ class WidgetGtk; namespace chromeos { class UserView; +class ExistingUserView; // UserController manages the set of windows needed to login a single existing // user or first time login for a new user. ExistingUserController creates @@ -55,8 +57,8 @@ class UserController : public views::ButtonListener, virtual ~Delegate() {} }; - // Creates a UserController representing the guest (other user) login. - explicit UserController(Delegate* delegate); + // Creates a UserController representing new user or bwsi login. + UserController(Delegate* delegate, bool is_bwsi); // Creates a UserController for the specified user. UserController(Delegate* delegate, const UserManager::User& user); @@ -66,7 +68,7 @@ class UserController : public views::ButtonListener, // Initializes the UserController, creating the set of windows/controls. // |index| is the index of this user, and |total_user_count| the total // number of users. - void Init(int index, int total_user_count); + void Init(int index, int total_user_count, bool need_browse_without_signin); // Update border window parameters to notify window manager about new numbers. // |index| of this user and |total_user_count| of users. @@ -74,7 +76,9 @@ class UserController : public views::ButtonListener, int user_index() const { return user_index_; } bool is_user_selected() const { return is_user_selected_; } - bool is_guest() const { return is_guest_; } + bool is_new_user() const { return is_new_user_; } + bool is_bwsi() const { return is_bwsi_; } + NewUserView* new_user_view() const { return new_user_view_; } const UserManager::User& user() const { return user_; } @@ -84,7 +88,11 @@ class UserController : public views::ButtonListener, // Resets password text and sets the enabled state of the password. void ClearAndEnablePassword(); + // Called when user view is activated (OnUserSelected). + void ClearAndEnableFields(); + // Returns bounds of password field in screen coordinates. + // For new user it returns username coordinates. gfx::Rect GetScreenBounds() const; // Get widget that contains all controls. @@ -97,7 +105,7 @@ class UserController : public views::ButtonListener, // Textfield::Controller: virtual void ContentsChanged(views::Textfield* sender, - const string16& new_contents) {} + const string16& new_contents); virtual bool HandleKeystroke(views::Textfield* sender, const views::Textfield::Keystroke& keystroke); @@ -118,13 +126,14 @@ class UserController : public views::ButtonListener, delegate_->AddStartUrl(start_url); } virtual void ClearErrors(); + virtual void NavigateAway(); // UserView::Delegate implementation: virtual void OnRemoveUser(); - virtual void OnChangePhoto(); - // Selects user entry with specified |index|. - void SelectUser(int index); + // Selects user entry with specified |index|, |is_click| is true if the entry + // was selected by mouse click. + void SelectUser(int index, bool is_click); // Sets focus on password field. void FocusPasswordField(); @@ -145,7 +154,9 @@ class UserController : public views::ButtonListener, const gfx::Rect& bounds, chromeos::WmIpcWindowType type, views::View* contents_view); - views::WidgetGtk* CreateControlsWindow(int index, int* height); + views::WidgetGtk* CreateControlsWindow(int index, + int* height, + bool need_browse_without_signin); views::WidgetGtk* CreateImageWindow(int index); views::WidgetGtk* CreateLabelWindow(int index, WmIpcWindowType type); void CreateBorderWindow(int index, int total_user_count, int controls_height); @@ -159,30 +170,34 @@ class UserController : public views::ButtonListener, // Sets the enabled state of the password field to |enable|. void SetPasswordEnabled(bool enable); + // Returns tooltip text for user name. + std::wstring GetNameTooltip() const; + + // Enable or disable the 'Submit' button based on the contents of + // |password_field_|. Enabled if there is text, otherwise disabled. + void UpdateSubmitButtonState(); + // User index within all the users. int user_index_; // Is this user selected now? bool is_user_selected_; - // Is this the guest user? - const bool is_guest_; + // Is this the new user pod? + const bool is_new_user_; + + // Is this the bwsi pod? + const bool is_bwsi_; // Should we show tooltips above user image and label to help distinguish // users with the same display name. bool show_name_tooltip_; - // If is_guest_ is false, this is the user being shown. + // If is_new_user_ and is_bwsi_ are false, this is the user being shown. UserManager::User user_; Delegate* delegate_; - // For editing the password. - views::Textfield* password_field_; - - // Button to start login. - views::NativeButton* submit_button_; - // A window is used to represent the individual chunks. views::WidgetGtk* controls_window_; views::WidgetGtk* image_window_; @@ -196,6 +211,9 @@ class UserController : public views::ButtonListener, // View that is used for new user login. NewUserView* new_user_view_; + // View that is used for existing user login. + ExistingUserView* existing_user_view_; + // Views that show display name of the user. views::Label* label_view_; views::Label* unselected_label_view_; diff --git a/chrome/browser/chromeos/login/user_image_downloader.cc b/chrome/browser/chromeos/login/user_image_downloader.cc index 83f5d45..3deea71 100644 --- a/chrome/browser/chromeos/login/user_image_downloader.cc +++ b/chrome/browser/chromeos/login/user_image_downloader.cc @@ -8,10 +8,11 @@ #include "base/logging.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" -#include "chrome/browser/chromeos/login/google_authenticator.h" +#include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/image_downloader.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/profile_manager.h" @@ -50,7 +51,7 @@ UserImageDownloader::UserImageDownloader(const std::string& username, profile_fetcher_->set_request_context( ProfileManager::GetDefaultProfile()->GetRequestContext()); profile_fetcher_->set_extra_request_headers( - StringPrintf(kAuthorizationHeader, auth_token_.c_str())); + base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); profile_fetcher_->Start(); } @@ -85,8 +86,12 @@ void UserImageDownloader::OnURLFetchComplete(const URLFetcher* source, void UserImageDownloader::OnImageDecoded(const SkBitmap& decoded_image) { // Save the image to file and its path to preferences. chromeos::UserManager* user_manager = chromeos::UserManager::Get(); - if (user_manager) + if (user_manager) { + if (user_manager->logged_in_user().email() == username_) { + user_manager->SetLoggedInUserImage(decoded_image); + } user_manager->SaveUserImage(username_, decoded_image); + } } bool UserImageDownloader::GetImageURL(const std::string& json_data, @@ -105,11 +110,11 @@ bool UserImageDownloader::GetImageURL(const std::string& json_data, DictionaryValue* root_dictionary = static_cast<DictionaryValue*>(root.get()); DictionaryValue* feed_dictionary = NULL; - if (!root_dictionary->GetDictionary(L"feed", &feed_dictionary)) + if (!root_dictionary->GetDictionary("feed", &feed_dictionary)) return false; ListValue* entry_list = NULL; - if (!feed_dictionary->GetList(L"entry", &entry_list)) + if (!feed_dictionary->GetList("entry", &entry_list)) return false; return GetImageURLFromEntries(entry_list, image_url); @@ -125,7 +130,7 @@ bool UserImageDownloader::GetImageURLFromEntries(ListValue* entry_list, continue; ListValue* email_list = NULL; - if (!entry_dictionary->GetList(L"gd$email", &email_list)) + if (!entry_dictionary->GetList("gd$email", &email_list)) continue; // Match entry email address to understand that this is user's entry. @@ -133,7 +138,7 @@ bool UserImageDownloader::GetImageURLFromEntries(ListValue* entry_list, continue; ListValue* link_list = NULL; - if (!entry_dictionary->GetList(L"link", &link_list)) + if (!entry_dictionary->GetList("link", &link_list)) continue; if (GetImageURLFromLinks(link_list, image_url)) @@ -153,7 +158,7 @@ bool UserImageDownloader::IsUserEntry(ListValue* email_list) const { if (!email_dictionary->GetStringASCII("address", &email)) continue; - if (GoogleAuthenticator::Canonicalize(email) == username_) + if (Authenticator::Canonicalize(email) == username_) return true; } return false; diff --git a/chrome/browser/chromeos/login/user_image_downloader.h b/chrome/browser/chromeos/login/user_image_downloader.h index f4de665..5eba206 100644 --- a/chrome/browser/chromeos/login/user_image_downloader.h +++ b/chrome/browser/chromeos/login/user_image_downloader.h @@ -4,12 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_DOWNLOADER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_DOWNLOADER_H_ +#pragma once #include <string> -#include <vector> #include "base/basictypes.h" -#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "chrome/browser/chromeos/login/image_decoder.h" #include "chrome/common/net/url_fetcher.h" diff --git a/chrome/browser/chromeos/login/user_image_loader.h b/chrome/browser/chromeos/login/user_image_loader.h index 454900b..4c7e0ad 100644 --- a/chrome/browser/chromeos/login/user_image_loader.h +++ b/chrome/browser/chromeos/login/user_image_loader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_LOADER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_LOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/user_image_screen.cc b/chrome/browser/chromeos/login/user_image_screen.cc index 9b617cc..a990f31 100644 --- a/chrome/browser/chromeos/login/user_image_screen.cc +++ b/chrome/browser/chromeos/login/user_image_screen.cc @@ -71,6 +71,7 @@ void UserImageScreen::OnOK(const SkBitmap& image) { UserManager* user_manager = UserManager::Get(); if (user_manager) { // TODO(avayvod): Check that there's logged in user actually. + user_manager->SetLoggedInUserImage(image); const UserManager::User& user = user_manager->logged_in_user(); user_manager->SaveUserImage(user.email(), image); } @@ -78,15 +79,8 @@ void UserImageScreen::OnOK(const SkBitmap& image) { delegate()->GetObserver(this)->OnExit(ScreenObserver::USER_IMAGE_SELECTED); } -void UserImageScreen::OnCancel() { - // Download user image from his Google account. - UserManager* user_manager = UserManager::Get(); - if (user_manager) { - // TODO(avayvod): Check that there's logged in user actually. - const UserManager::User& user = user_manager->logged_in_user(); - new UserImageDownloader(user.email(), - LoginUtils::Get()->GetAuthToken()); - } +void UserImageScreen::OnSkip() { + // TODO(avayvod): Use one of the default images. See http://crosbug.com/5780. if (delegate()) delegate()->GetObserver(this)->OnExit(ScreenObserver::USER_IMAGE_SKIPPED); } diff --git a/chrome/browser/chromeos/login/user_image_screen.h b/chrome/browser/chromeos/login/user_image_screen.h index 609e5f6..5dd0142 100644 --- a/chrome/browser/chromeos/login/user_image_screen.h +++ b/chrome/browser/chromeos/login/user_image_screen.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_H_ +#pragma once #include "chrome/browser/chromeos/login/camera.h" #include "chrome/browser/chromeos/login/user_image_view.h" @@ -32,7 +33,7 @@ class UserImageScreen: public ViewScreen<UserImageView>, // UserImageView::Delegate implementation: virtual void OnOK(const SkBitmap& image); - virtual void OnCancel(); + virtual void OnSkip(); // NotificationObserver implementation: virtual void Observe(NotificationType type, diff --git a/chrome/browser/chromeos/login/user_image_view.cc b/chrome/browser/chromeos/login/user_image_view.cc index eb93af3..dfd89ea 100644 --- a/chrome/browser/chromeos/login/user_image_view.cc +++ b/chrome/browser/chromeos/login/user_image_view.cc @@ -9,8 +9,10 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/callback.h" -#include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" +#include "chrome/browser/profile_manager.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -19,6 +21,7 @@ #include "views/controls/button/native_button.h" #include "views/controls/image_view.h" #include "views/controls/label.h" +#include "views/grid_layout.h" namespace { @@ -30,8 +33,15 @@ const int kVerticalMargin = 10; const int kHorizontalPadding = 10; // Padding between vertically neighboring elements. const int kVerticalPadding = 10; -// Size for button with video image. -const int kVideoImageSize = 256; +// Size for user image shown on the screen. +const int kUserImageSize = 256; + +// IDs of column sets for grid layout manager. +enum ColumnSets { + kTitleRow, // Column set for screen title. + kImageRow, // Column set for image from camera and snapshot button. + kButtonsRow, // Column set for Skip and OK buttons. +}; } // namespace @@ -42,11 +52,11 @@ using login::kUserImageSize; UserImageView::UserImageView(Delegate* delegate) : title_label_(NULL), ok_button_(NULL), - cancel_button_(NULL), - video_button_(NULL), - selected_image_(NULL), + skip_button_(NULL), + snapshot_button_(NULL), + user_image_(NULL), delegate_(delegate), - image_selected_(false) { + is_capturing_(true) { } UserImageView::~UserImageView() { @@ -59,158 +69,107 @@ void UserImageView::Init() { &BorderDefinition::kScreenBorder); set_background(views::Background::CreateBackgroundPainter(true, painter)); - // Set up fonts. - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - gfx::Font title_font = rb.GetFont(ResourceBundle::MediumBoldFont); - - title_label_ = new views::Label(); + title_label_ = + new views::Label(l10n_util::GetString(IDS_USER_IMAGE_SCREEN_TITLE)); title_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - title_label_->SetFont(title_font); title_label_->SetMultiLine(true); - AddChildView(title_label_); - - SkBitmap video_button_image = - skia::ImageOperations::Resize( - *ResourceBundle::GetSharedInstance().GetBitmapNamed( - IDR_USER_IMAGE_NO_VIDEO), - skia::ImageOperations::RESIZE_BOX, - kVideoImageSize, - kVideoImageSize); - - video_button_ = new views::ImageButton(this); - video_button_->SetImage(views::CustomButton::BS_NORMAL, &video_button_image); - AddChildView(video_button_); - selected_image_ = new views::ImageView(); - selected_image_->SetImageSize( + user_image_ = new views::ImageView(); + user_image_->SetImageSize( gfx::Size(kUserImageSize, kUserImageSize)); - selected_image_->SetImage( - *ResourceBundle::GetSharedInstance().GetBitmapNamed( - IDR_LOGIN_OTHER_USER)); - AddChildView(selected_image_); + user_image_->SetImage( + ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_USER_IMAGE_NO_VIDEO)); - UpdateLocalizedStrings(); -} + snapshot_button_ = new views::ImageButton(this); + snapshot_button_->SetFocusable(true); + snapshot_button_->SetImage(views::CustomButton::BS_NORMAL, + ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_USER_IMAGE_CAPTURE)); -void UserImageView::RecreateNativeControls() { - // There is no way to get native button preferred size after the button was - // sized so delete and recreate the button on text update. - delete ok_button_; - ok_button_ = new views::NativeButton(this, std::wstring()); - AddChildView(ok_button_); - ok_button_->SetEnabled(image_selected_); - if (image_selected_) - ok_button_->RequestFocus(); - - delete cancel_button_; - cancel_button_ = new views::NativeButton(this, std::wstring()); - AddChildView(cancel_button_); - cancel_button_->SetEnabled(true); -} + ok_button_ = new views::NativeButton(this, l10n_util::GetString(IDS_OK)); + ok_button_->SetEnabled(!is_capturing_); -void UserImageView::UpdateLocalizedStrings() { - RecreateNativeControls(); + skip_button_ = new views::NativeButton(this, l10n_util::GetString(IDS_SKIP)); + skip_button_->SetEnabled(true); - title_label_->SetText(l10n_util::GetString(IDS_USER_IMAGE_SCREEN_TITLE)); - ok_button_->SetLabel(l10n_util::GetString(IDS_OK)); - cancel_button_->SetLabel(l10n_util::GetString(IDS_CANCEL)); - selected_image_->SetTooltipText( - l10n_util::GetString(IDS_USER_IMAGE_SELECTED_TOOLTIP)); + InitLayout(); + // Request focus only after the button is added to views hierarchy. + snapshot_button_->RequestFocus(); } -void UserImageView::UpdateVideoFrame(const SkBitmap& frame) { - last_frame_.reset(new SkBitmap(frame)); - SkBitmap video_button_image = - skia::ImageOperations::Resize( - *last_frame_, - skia::ImageOperations::RESIZE_BOX, - kVideoImageSize, - kVideoImageSize); - - video_button_->SetImage(views::CustomButton::BS_NORMAL, &video_button_image); - video_button_->SchedulePaint(); +void UserImageView::InitLayout() { + views::GridLayout* layout = new views::GridLayout(this); + layout->SetInsets(GetInsets()); + SetLayoutManager(layout); + + // The title is left-top aligned. + views::ColumnSet* column_set = layout->AddColumnSet(kTitleRow); + column_set->AddPaddingColumn(0, kHorizontalMargin); + column_set->AddColumn(views::GridLayout::LEADING, + views::GridLayout::LEADING, + 1, + views::GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kHorizontalMargin); + + // User image and snapshot button are centered horizontally. + column_set = layout->AddColumnSet(kImageRow); + column_set->AddPaddingColumn(0, kHorizontalMargin); + column_set->AddColumn(views::GridLayout::CENTER, + views::GridLayout::LEADING, + 1, + views::GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kHorizontalMargin); + + // OK and Skip buttons are in the right bottom corner of the view. + column_set = layout->AddColumnSet(kButtonsRow); + column_set->AddPaddingColumn(0, kHorizontalMargin); + // This column takes the empty space to the left of the buttons. + column_set->AddPaddingColumn(1, 1); + column_set->AddColumn(views::GridLayout::TRAILING, + views::GridLayout::TRAILING, + 0, + views::GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kHorizontalPadding); + column_set->AddColumn(views::GridLayout::TRAILING, + views::GridLayout::TRAILING, + 0, + views::GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kHorizontalMargin); + + // Fill the layout with rows and views now. + layout->StartRowWithPadding(0, kTitleRow, 0, kVerticalMargin); + layout->AddView(title_label_); + layout->StartRowWithPadding(0, kImageRow, 0, kVerticalPadding); + layout->AddView(user_image_); + layout->StartRowWithPadding(1, kImageRow, 0, kVerticalPadding); + layout->AddView(snapshot_button_); + layout->StartRowWithPadding(0, kButtonsRow, 0, kVerticalPadding); + layout->AddView(skip_button_); + layout->AddView(ok_button_); + layout->AddPaddingRow(0, kVerticalMargin); } -void UserImageView::OnVideoImageClicked() { - // TODO(avayvod): Snapshot sound. - if (!last_frame_.get()) +void UserImageView::UpdateVideoFrame(const SkBitmap& frame) { + if (!is_capturing_) return; - selected_image_->SetImage( + last_frame_.reset(new SkBitmap(frame)); + SkBitmap user_image = skia::ImageOperations::Resize( *last_frame_, - skia::ImageOperations::RESIZE_LANCZOS3, + skia::ImageOperations::RESIZE_BOX, kUserImageSize, - kUserImageSize)); - image_selected_ = true; - ok_button_->SetEnabled(true); - ok_button_->RequestFocus(); -} + kUserImageSize); -void UserImageView::LocaleChanged() { - UpdateLocalizedStrings(); - Layout(); + user_image_->SetImage(&user_image); } -void UserImageView::Layout() { - gfx::Insets insets = GetInsets(); - - // Place title at the top. - int title_x = insets.left() + kHorizontalMargin; - int title_y = insets.top() + kVerticalMargin; - int max_width = width() - insets.width() - kHorizontalMargin * 2; - title_label_->SizeToFit(max_width); - - gfx::Size title_size = title_label_->GetPreferredSize(); - title_label_->SetBounds(title_x, - title_y, - std::min(max_width, title_size.width()), - title_size.height()); - - // Put OK button at the right bottom corner. - gfx::Size ok_size = ok_button_->GetPreferredSize(); - int ok_x = width() - insets.right() - kHorizontalMargin - ok_size.width(); - int ok_y = height() - insets.bottom() - kVerticalMargin - ok_size.height(); - ok_button_->SetBounds(ok_x, ok_y, ok_size.width(), ok_size.height()); - - // Put Cancel button to the left from OK. - gfx::Size cancel_size = cancel_button_->GetPreferredSize(); - int cancel_x = ok_x - kHorizontalPadding - cancel_size.width(); - int cancel_y = ok_y; // Height should be the same for both buttons. - cancel_button_->SetBounds(cancel_x, - cancel_y, - cancel_size.width(), - cancel_size.height()); - - // The area between buttons and title is for images. - int title_bottom = title_label_->y() + title_label_->height(); - gfx::Rect images_area(insets.left() + kHorizontalMargin, - title_bottom + kVerticalPadding, - max_width, - ok_button_->y() - title_bottom - - 2 * kVerticalPadding); - - // Selected image is floating in the middle between top and height, near - // the right border. - gfx::Size selected_image_size = selected_image_->GetPreferredSize(); - int selected_image_x = images_area.right() - selected_image_size.width(); - int selected_image_y = images_area.y() + - (images_area.height() - selected_image_size.height()) / 2; - selected_image_->SetBounds(selected_image_x, - selected_image_y, - selected_image_size.width(), - selected_image_size.height()); - - // Video capture image is on the left side of the area, top aligned with - // selected image. - int video_button_x = images_area.x(); - int video_button_y = selected_image_y; - video_button_->SetBounds(video_button_x, - video_button_y, - kVideoImageSize, - kVideoImageSize); - - SchedulePaint(); +void UserImageView::ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child) { + if (is_add && this == child) + WizardAccessibilityHelper::GetInstance()->MaybeEnableAccessibility(this); } gfx::Size UserImageView::GetPreferredSize() { @@ -220,16 +179,30 @@ gfx::Size UserImageView::GetPreferredSize() { void UserImageView::ButtonPressed( views::Button* sender, const views::Event& event) { DCHECK(delegate_); - if (sender == video_button_) { - OnVideoImageClicked(); - return; - } - if (sender == ok_button_) - delegate_->OnOK(selected_image_->GetImage()); - else if (sender == cancel_button_) - delegate_->OnCancel(); - else + if (sender == snapshot_button_) { + if (is_capturing_) { + ok_button_->SetEnabled(true); + ok_button_->RequestFocus(); + snapshot_button_->SetImage( + views::CustomButton::BS_NORMAL, + ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_USER_IMAGE_RECYCLE)); + } else { + ok_button_->SetEnabled(false); + snapshot_button_->SetImage( + views::CustomButton::BS_NORMAL, + ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_USER_IMAGE_CAPTURE)); + } + snapshot_button_->SchedulePaint(); + is_capturing_ = !is_capturing_; + } else if (sender == ok_button_) { + delegate_->OnOK(user_image_->GetImage()); + } else if (sender == skip_button_) { + delegate_->OnSkip(); + } else { NOTREACHED(); + } } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_view.h b/chrome/browser/chromeos/login/user_image_view.h index 028499d..7f59381 100644 --- a/chrome/browser/chromeos/login/user_image_view.h +++ b/chrome/browser/chromeos/login/user_image_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_VIEW_H_ +#pragma once #include "views/controls/button/button.h" #include "views/view.h" @@ -11,10 +12,10 @@ class SkBitmap; namespace views { +class ImageButton; class ImageView; class Label; class NativeButton; -class ImageButton; } // namespace views namespace chromeos { @@ -33,51 +34,46 @@ class UserImageView : public views::View, virtual void OnOK(const SkBitmap& image) = 0; // Called if user decides to skip image selection screen. - virtual void OnCancel() = 0; + virtual void OnSkip() = 0; }; explicit UserImageView(Delegate* delegate); virtual ~UserImageView(); - // Initialize view layout. + // Initializes this view, its children and layout. void Init(); - // Update strings from the resources. Executed on language change. - void UpdateLocalizedStrings(); - - // Updates snapshot from camera on camera image view. + // Updates image from camera. void UpdateVideoFrame(const SkBitmap& frame); - // Called when user left-clicks video image control. - void OnVideoImageClicked(); - // Overridden from views::View: virtual gfx::Size GetPreferredSize(); - virtual void Layout(); // Overridden from views::ButtonListener. virtual void ButtonPressed(views::Button* sender, const views::Event& event); protected: // views::View overrides: - virtual void LocaleChanged(); + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); private: - // Delete and recreate native controls that fail to update preferred size - // after string update. - void RecreateNativeControls(); + // Initializes layout manager for this view. + void InitLayout(); views::Label* title_label_; views::NativeButton* ok_button_; - views::NativeButton* cancel_button_; - views::ImageButton* video_button_; - views::ImageView* selected_image_; + views::NativeButton* skip_button_; + views::ImageButton* snapshot_button_; + views::ImageView* user_image_; // Notifications receiver. Delegate* delegate_; - // Indicates that some image was selected and OK can be pressed. - bool image_selected_; + // Indicates that we're in capturing mode. When |false|, new video frames + // are not shown to user if received. + bool is_capturing_; // Last frame that was received from the camera in its original resolution. scoped_ptr<SkBitmap> last_frame_; diff --git a/chrome/browser/chromeos/login/user_manager.cc b/chrome/browser/chromeos/login/user_manager.cc index f0c18a8..f6edcbe 100644 --- a/chrome/browser/chromeos/login/user_manager.cc +++ b/chrome/browser/chromeos/login/user_manager.cc @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/nss_util.h" #include "base/path_service.h" #include "base/string_util.h" #include "base/time.h" @@ -16,10 +17,14 @@ #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/input_method_library.h" +#include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/wm_ipc.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" #include "gfx/codec/png_codec.h" #include "grit/theme_resources.h" @@ -28,9 +33,13 @@ namespace chromeos { namespace { // A vector pref of the users who have logged into the device. -const wchar_t kLoggedInUsers[] = L"LoggedInUsers"; +const char kLoggedInUsers[] = "LoggedInUsers"; // A dictionary that maps usernames to file paths to their images. -const wchar_t kUserImages[] = L"UserImages"; +const char kUserImages[] = "UserImages"; + +// Incognito user is represented by an empty string (since some code already +// depends on that and it's hard to figure out what). +const char kIncognitoUser[] = ""; // The one true UserManager. static UserManager* user_manager_ = NULL; @@ -42,8 +51,7 @@ void save_path_to_local_state(const std::string& username, PrefService* local_state = g_browser_process->local_state(); DictionaryValue* images = local_state->GetMutableDictionary(kUserImages); - images->SetWithoutPathExpansion(UTF8ToWide(username), - new StringValue(image_path)); + images->SetWithoutPathExpansion(username, new StringValue(image_path)); LOG(INFO) << "Saving path to user image in Local State."; local_state->SavePersistentPrefs(); } @@ -74,6 +82,14 @@ void save_image_to_file(const SkBitmap& image, username, image_path.value())); } +// Checks current user's ownership on file thread. +void CheckOwnership() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + + UserManager::Get()->set_current_user_is_owner( + OwnershipService::GetSharedInstance()->CurrentUserIsOwner()); +} + } // namespace UserManager::User::User() { @@ -124,8 +140,7 @@ std::vector<UserManager::User> UserManager::GetUsers() const { std::string image_path; if (image_it == user_images_.end()) { if (prefs_images && - prefs_images->GetStringWithoutPathExpansion( - ASCIIToWide(email), &image_path)) { + prefs_images->GetStringWithoutPathExpansion(email, &image_path)) { // Insert the default image so we don't send another request if // GetUsers is called twice. user_images_[email] = user.image(); @@ -142,10 +157,17 @@ std::vector<UserManager::User> UserManager::GetUsers() const { } void UserManager::OffTheRecordUserLoggedIn() { + logged_in_user_ = User(); + logged_in_user_.set_email(kIncognitoUser); NotifyOnLogin(); } void UserManager::UserLoggedIn(const std::string& email) { + if (email == kIncognitoUser) { + OffTheRecordUserLoggedIn(); + return; + } + // Get a copy of the current users. std::vector<User> users = GetUsers(); @@ -193,6 +215,25 @@ void UserManager::RemoveUser(const std::string& email) { prefs->SavePersistentPrefs(); } +bool UserManager::IsKnownUser(const std::string& email) { + std::vector<User> users = GetUsers(); + for (std::vector<User>::iterator it = users.begin(); + it < users.end(); + ++it) { + if (it->email() == email) + return true; + } + + return false; +} + +void UserManager::SetLoggedInUserImage(const SkBitmap& image) { + if (logged_in_user_.email().empty()) + return; + logged_in_user_.set_image(image); + OnImageLoaded(logged_in_user_.email(), image); +} + void UserManager::SaveUserImage(const std::string& username, const SkBitmap& image) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); @@ -224,7 +265,10 @@ void UserManager::OnImageLoaded(const std::string& username, // Private constructor and destructor. Do nothing. UserManager::UserManager() - : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))) { + : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))), + current_user_is_owner_(false) { + registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED, + NotificationService::AllSources()); } UserManager::~UserManager() { @@ -237,8 +281,28 @@ void UserManager::NotifyOnLogin() { Source<UserManager>(this), Details<const User>(&logged_in_user_)); + chromeos::CrosLibrary::Get()->GetInputMethodLibrary()-> + SetDeferImeStartup(false); + // Shut down the IME so that it will reload the user's settings. + chromeos::CrosLibrary::Get()->GetInputMethodLibrary()-> + StopInputMethodProcesses(); // Let the window manager know that we're logged in now. WmIpc::instance()->SetLoggedInProperty(true); + // Ensure we've opened the real user's key/certificate database. + base::OpenPersistentNSSDB(); + + // Schedules current user ownership check on file thread. + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, + NewRunnableFunction(&CheckOwnership)); +} + +void UserManager::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) { + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, + NewRunnableFunction(&CheckOwnership)); + } } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_manager.h b/chrome/browser/chromeos/login/user_manager.h index 0e4ce31..d3e40be 100644 --- a/chrome/browser/chromeos/login/user_manager.h +++ b/chrome/browser/chromeos/login/user_manager.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_MANAGER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_MANAGER_H_ +#pragma once #include <string> #include <vector> @@ -12,6 +13,8 @@ #include "base/hash_tables.h" #include "base/ref_counted.h" #include "chrome/browser/chromeos/login/user_image_loader.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "third_party/skia/include/core/SkBitmap.h" class PrefService; @@ -20,7 +23,8 @@ namespace chromeos { // This class provides a mechanism for discovering users who have logged // into this chromium os device before and updating that list. -class UserManager : public UserImageLoader::Delegate { +class UserManager : public UserImageLoader::Delegate, + public NotificationObserver { public: // A class representing information about a previously logged in user. class User { @@ -65,11 +69,18 @@ class UserManager : public UserImageLoader::Delegate { // Remove user from persistent list. NOTE: user's data won't be removed. void RemoveUser(const std::string& email); + // Returns true if given user has logged into the device before. + bool IsKnownUser(const std::string& email); + // Returns the logged-in user. const User& logged_in_user() { return logged_in_user_; } + // Sets image for logged-in user and sends LOGIN_USER_IMAGE_CHANGED + // notification about the image changed via NotificationService. + void SetLoggedInUserImage(const SkBitmap& image); + // Saves image to file and saves image path in local state preferences. void SaveUserImage(const std::string& username, const SkBitmap& image); @@ -78,6 +89,19 @@ class UserManager : public UserImageLoader::Delegate { virtual void OnImageLoaded(const std::string& username, const SkBitmap& image); + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Accessor for current_user_is_owner_ + bool current_user_is_owner() const { + return current_user_is_owner_; + } + void set_current_user_is_owner(bool current_user_is_owner) { + current_user_is_owner_ = current_user_is_owner; + } + private: UserManager(); ~UserManager(); @@ -95,6 +119,11 @@ class UserManager : public UserImageLoader::Delegate { // The logged-in user. User logged_in_user_; + // Cached flag of whether currently logged-in user is owner or not. + bool current_user_is_owner_; + + NotificationRegistrar registrar_; + DISALLOW_COPY_AND_ASSIGN(UserManager); }; diff --git a/chrome/browser/chromeos/login/user_view.cc b/chrome/browser/chromeos/login/user_view.cc index 10db8b0..77afcb9 100644 --- a/chrome/browser/chromeos/login/user_view.cc +++ b/chrome/browser/chromeos/login/user_view.cc @@ -7,12 +7,14 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" #include "views/controls/button/menu_button.h" #include "views/controls/button/text_button.h" #include "views/controls/image_view.h" #include "views/controls/label.h" -#include "views/controls/menu/menu_2.h" +#include "views/controls/link.h" #include "views/controls/throbber.h" namespace { @@ -20,28 +22,25 @@ namespace { // Background color of the login status label and signout button. const SkColor kSignoutBackgroundColor = 0xFF007700; -// Left margin to the "Active User" text. -const int kSignoutLeftOffset = 10; +// Horiz/Vert insets for Signout view. +const int kSignoutViewHorizontalInsets = 10; +const int kSignoutViewVerticalInsets = 5; -// Padding between menu button and left right image corner. -const int kMenuButtonPadding = 4; - -const int kIdRemove = 1; -const int kIdChangePhoto = 2; +// Padding between remove button and top right image corner. +const int kRemoveButtonPadding = 3; } // namespace namespace chromeos { using login::kBackgroundColor; -using login::kBorderSize; using login::kTextColor; using login::kUserImageSize; // The view that shows the Sign out button below the user's image. class SignoutView : public views::View { public: - explicit SignoutView(views::ButtonListener* listener) { + explicit SignoutView(views::LinkController* link_controller) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); const gfx::Font& font = rb.GetFont(ResourceBundle::SmallFont); @@ -50,16 +49,15 @@ class SignoutView : public views::View { active_user_label_->SetFont(font); active_user_label_->SetColor(kTextColor); - signout_button_ = new views::TextButton( - listener, l10n_util::GetString(IDS_SCREEN_LOCK_SIGN_OUT)); - signout_button_->SetFont(font); - signout_button_->SetEnabledColor(kTextColor); - signout_button_->SetNormalHasBorder(false); - signout_button_->set_tag(login::SIGN_OUT); - signout_button_->SetFocusable(true); + signout_link_ = new views::Link( + l10n_util::GetString(IDS_SCREEN_LOCK_SIGN_OUT)); + signout_link_->SetController(link_controller); + signout_link_->SetFont(font); + signout_link_->SetColor(kTextColor); + signout_link_->SetFocusable(true); AddChildView(active_user_label_); - AddChildView(signout_button_); + AddChildView(signout_link_); set_background(views::Background::CreateSolidBackground( kSignoutBackgroundColor)); @@ -68,51 +66,133 @@ class SignoutView : public views::View { // views::View overrides. virtual void Layout() { gfx::Size label = active_user_label_->GetPreferredSize(); - gfx::Size button = signout_button_->GetPreferredSize(); - active_user_label_->SetBounds(kSignoutLeftOffset, - (height() - label.height()) / 2, - label.width(), label.height()); - signout_button_->SetBounds( - width() - button.width(), (height() - button.height()) / 2, + gfx::Size button = signout_link_->GetPreferredSize(); + active_user_label_->SetBounds( + kSignoutViewHorizontalInsets, (height() - label.height()) / 2, + label.width(), label.height()); + signout_link_->SetBounds( + width() - button.width() - kSignoutViewHorizontalInsets, + (height() - button.height()) / 2, button.width(), button.height()); } virtual gfx::Size GetPreferredSize() { gfx::Size label = active_user_label_->GetPreferredSize(); - gfx::Size button = signout_button_->GetPreferredSize(); + gfx::Size button = signout_link_->GetPreferredSize(); return gfx::Size(label.width() + button.width(), - std::max(label.height(), button.height())); + std::max(label.height(), button.height()) + + kSignoutViewVerticalInsets * 2); } - views::Button* signout_button() { return signout_button_; } + views::Link* signout_link() { return signout_link_; } private: friend class UserView; views::Label* active_user_label_; - views::TextButton* signout_button_; + views::Link* signout_link_; DISALLOW_COPY_AND_ASSIGN(SignoutView); }; -UserView::UserView(Delegate* delegate, bool is_login) +class RemoveButton : public views::TextButton { + public: + RemoveButton(views::ButtonListener* listener, + const SkBitmap& icon, + const std::wstring& text, + const gfx::Point& top_right) + : views::TextButton(listener, std::wstring()), + icon_(icon), + text_(text), + top_right_(top_right) { + SetEnabledColor(SK_ColorWHITE); + SetDisabledColor(SK_ColorWHITE); + SetHighlightColor(SK_ColorWHITE); + SetHoverColor(SK_ColorWHITE); + SetIcon(icon_); + set_border(NULL); + UpdatePosition(); + } + + protected: + // Overridden from View: + virtual void OnMouseEntered(const views::MouseEvent& e) { + SetIcon(SkBitmap()); + views::TextButton::SetText(text_); + + const SkColor kStrokeColor = SK_ColorWHITE; + const SkColor kButtonColor = 0xFFE94949; + const int kStrokeWidth = 1; + const int kVerticalPadding = 4; + const int kHorizontalPadding = 8; + const int kCornerRadius = 4; + + set_background( + CreateRoundedBackground( + kCornerRadius, kStrokeWidth, kButtonColor, kStrokeColor)); + + set_border( + views::Border::CreateEmptyBorder(kVerticalPadding, + kHorizontalPadding, + kVerticalPadding, + kHorizontalPadding)); + + UpdatePosition(); + } + + void OnMouseMoved(const views::MouseEvent& e) { + } + + virtual void OnMouseExited(const views::MouseEvent& e) { + SetIcon(icon_); + views::TextButton::SetText(std::wstring()); + ClearMaxTextSize(); + set_background(NULL); + set_border(NULL); + UpdatePosition(); + } + + void SetText(const std::wstring& text) { + text_ = text; + } + + private: + // Update button position and schedule paint event for the view and parent. + void UpdatePosition() { + gfx::Size size = GetPreferredSize(); + gfx::Point origin = top_right_; + origin.Offset(-size.width(), 0); + SetBounds(gfx::Rect(origin, size)); + + if (GetParent()) + GetParent()->SchedulePaint(); + } + + SkBitmap icon_; + std::wstring text_; + gfx::Point top_right_; + + DISALLOW_COPY_AND_ASSIGN(RemoveButton); +}; + +UserView::UserView(Delegate* delegate, bool is_login, bool need_background) : delegate_(delegate), signout_view_(NULL), image_view_(new views::ImageView()), throbber_(CreateDefaultSmoothedThrobber()), - menu_button_(NULL) { + remove_button_(NULL) { DCHECK(delegate); if (!is_login) signout_view_ = new SignoutView(this); - if (is_login) - menu_button_ = new views::MenuButton(NULL, std::wstring(), this, true); - Init(); + Init(need_background); } -void UserView::Init() { - image_view_->set_background( - views::Background::CreateSolidBackground(kBackgroundColor)); +void UserView::Init(bool need_background) { + if (need_background) { + image_view_->set_background( + views::Background::CreateSolidBackground(kBackgroundColor)); + } if (throbber_) { int w = throbber_->GetPreferredSize().width(); int h = throbber_->GetPreferredSize().height(); @@ -132,14 +212,15 @@ void UserView::Init() { signout_view_->GetPreferredSize().height()); AddChildView(signout_view_); } - if (menu_button_) { - int w = menu_button_->GetPreferredSize().width(); - int h = menu_button_->GetPreferredSize().height(); - menu_button_->SetBounds(kUserImageSize - w - kMenuButtonPadding, - kMenuButtonPadding, w, h); - menu_button_->SetVisible(false); - AddChildView(menu_button_); - } + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + remove_button_ = new RemoveButton( + this, + *rb.GetBitmapNamed(IDR_CLOSE_BAR_H), + l10n_util::GetString(IDS_LOGIN_REMOVE), + gfx::Point(kUserImageSize - kRemoveButtonPadding, kRemoveButtonPadding)); + remove_button_->SetVisible(false); + AddChildView(remove_button_); } void UserView::SetImage(const SkBitmap& image) { @@ -175,58 +256,28 @@ gfx::Size UserView::GetPreferredSize() { void UserView::SetSignoutEnabled(bool enabled) { DCHECK(signout_view_); - signout_view_->signout_button_->SetEnabled(enabled); + signout_view_->signout_link_->SetEnabled(enabled); } -void UserView::ButtonPressed(views::Button* sender, const views::Event& event) { +void UserView::LinkActivated(views::Link* source, int event_flags) { DCHECK(delegate_); - if (sender->tag() == login::SIGN_OUT) + DCHECK(signout_view_); + if (signout_view_->signout_link_ == source) delegate_->OnSignout(); } -void UserView::SetMenuVisible(bool flag) { - DCHECK(menu_button_); - menu_button_->SetVisible(flag); -} - -void UserView::BuildMenu() { - menu_model_.reset(new menus::SimpleMenuModel(this)); - menu_model_->AddItemWithStringId(kIdRemove, IDS_LOGIN_REMOVE); - menu_model_->AddItemWithStringId(kIdChangePhoto, IDS_LOGIN_CHANGE_PHOTO); - menu_.reset(new views::Menu2(menu_model_.get())); -} - -void UserView::RunMenu(View* source, const gfx::Point& pt) { - if (!menu_.get()) - BuildMenu(); - - menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); -} - -bool UserView::IsCommandIdChecked(int command_id) const { - return false; -} - -bool UserView::IsCommandIdEnabled(int command_id) const { - // TODO(dpolukhin): implement and enable change photo. - return command_id != kIdChangePhoto; +void UserView::SetRemoveButtonVisible(bool flag) { + remove_button_->SetVisible(flag); } -bool UserView::GetAcceleratorForCommandId(int command_id, - menus::Accelerator* accelerator) { - return false; +void UserView::ButtonPressed(views::Button* sender, const views::Event& event) { + DCHECK(delegate_); + if (remove_button_ == sender) + delegate_->OnRemoveUser(); } -void UserView::ExecuteCommand(int command_id) { - switch (command_id) { - case kIdRemove: - delegate_->OnRemoveUser(); - break; - - case kIdChangePhoto: - delegate_->OnChangePhoto(); - break; - } +void UserView::OnLocaleChanged() { + remove_button_->SetText(l10n_util::GetString(IDS_LOGIN_REMOVE)); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_view.h b/chrome/browser/chromeos/login/user_view.h index 25eb9d9..772910c 100644 --- a/chrome/browser/chromeos/login/user_view.h +++ b/chrome/browser/chromeos/login/user_view.h @@ -4,20 +4,19 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_USER_VIEW_H_ +#pragma once #include <string> -#include "app/menus/simple_menu_model.h" #include "views/controls/button/button.h" -#include "views/controls/menu/view_menu_delegate.h" +#include "views/controls/link.h" #include "views/view.h" class SkBitmap; namespace views { class ImageView; -class Menu2; -class MenuButton; +class TextButton; class Throbber; } // namespace views @@ -26,9 +25,8 @@ namespace chromeos { class SignoutView; class UserView : public views::View, - public views::ButtonListener, - public views::ViewMenuDelegate, - public menus::SimpleMenuModel::Delegate { + public views::LinkController, + public views::ButtonListener { public: class Delegate { public: @@ -39,19 +37,17 @@ class UserView : public views::View, // Notifies that user would like to remove this user from login screen. virtual void OnRemoveUser() {} - - // Notifies that user would like to take new picture for this user on - // login screen. - virtual void OnChangePhoto() {} }; // Creates UserView for login screen (|is_login| == true) or screen locker. // On login screen this will have addition menu with user specific actions. - // On screen locker it will have sign out button. - UserView(Delegate* delegate, bool is_login); + // On screen locker it will have sign out button. |need_background| is needed + // to show image with transparent areas. + UserView(Delegate* delegate, bool is_login, bool need_background); // view::View overrides. virtual gfx::Size GetPreferredSize(); + virtual void OnLocaleChanged(); // Sets the user's image. void SetImage(const SkBitmap& image); @@ -63,27 +59,21 @@ class UserView : public views::View, void StartThrobber(); void StopThrobber(); - // Show/Hide menu for user specific actions. - void SetMenuVisible(bool flag); + // Show/Hide remove button. + void SetRemoveButtonVisible(bool flag); // Enable/Disable sign-out button. void SetSignoutEnabled(bool enabled); - // ButtonListener: - virtual void ButtonPressed(views::Button* sender, const views::Event& event); - - // ViewMenuDelegate: - virtual void RunMenu(View* source, const gfx::Point& pt); + // Implements LinkController. + // Called when a signout link is clicked. + virtual void LinkActivated(views::Link* source, int event_flags); - // menus::SimpleMenuModel::Delegate: - virtual bool IsCommandIdChecked(int command_id) const; - virtual bool IsCommandIdEnabled(int command_id) const; - virtual bool GetAcceleratorForCommandId(int command_id, - menus::Accelerator* accelerator); - virtual void ExecuteCommand(int command_id); + // Overridden from views::ButtonListener. + virtual void ButtonPressed(views::Button* sender, const views::Event& event); private: - void Init(); + void Init(bool need_background); void BuildMenu(); Delegate* delegate_; @@ -95,10 +85,7 @@ class UserView : public views::View, views::Throbber* throbber_; - // Menu for user specific actions. - scoped_ptr<menus::SimpleMenuModel> menu_model_; - scoped_ptr<views::Menu2> menu_; - views::MenuButton* menu_button_; + views::TextButton* remove_button_; DISALLOW_COPY_AND_ASSIGN(UserView); }; @@ -106,4 +93,3 @@ class UserView : public views::View, } // chromeos #endif // CHROME_BROWSER_CHROMEOS_LOGIN_USER_VIEW_H_ - diff --git a/chrome/browser/chromeos/login/view_screen.h b/chrome/browser/chromeos/login/view_screen.h index ea9d437..81586e4 100644 --- a/chrome/browser/chromeos/login/view_screen.h +++ b/chrome/browser/chromeos/login/view_screen.h @@ -4,19 +4,27 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_VIEW_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_VIEW_SCREEN_H_ +#pragma once #include "base/message_loop.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/wizard_screen.h" +#include "gfx/size.h" template <class V> class ViewScreen : public WizardScreen { public: + // Create screen with default size. explicit ViewScreen(WizardScreenDelegate* delegate); + + // Create screen with the specified size. + ViewScreen(WizardScreenDelegate* delegate, int width, int height); virtual ~ViewScreen(); // Overridden from WizardScreen: virtual void Show(); virtual void Hide(); + virtual gfx::Size GetScreenSize() const { return size_; } V* view() { return view_; } @@ -35,6 +43,9 @@ class ViewScreen : public WizardScreen { V* view_; + // Size of the screen. + gfx::Size size_; + DISALLOW_COPY_AND_ASSIGN(ViewScreen); }; @@ -53,7 +64,16 @@ class DefaultViewScreen : public ViewScreen<V> { template <class V> ViewScreen<V>::ViewScreen(WizardScreenDelegate* delegate) : WizardScreen(delegate), - view_(NULL) { + view_(NULL), + size_(chromeos::login::kWizardScreenWidth, + chromeos::login::kWizardScreenHeight) { +} + +template <class V> +ViewScreen<V>::ViewScreen(WizardScreenDelegate* delegate, int width, int height) + : WizardScreen(delegate), + view_(NULL), + size_(width, height) { } template <class V> diff --git a/chrome/browser/chromeos/login/web_page_screen.h b/chrome/browser/chromeos/login/web_page_screen.h index 5c9bc17..c9746bf 100644 --- a/chrome/browser/chromeos/login/web_page_screen.h +++ b/chrome/browser/chromeos/login/web_page_screen.h @@ -4,8 +4,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WEB_PAGE_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_WEB_PAGE_SCREEN_H_ +#pragma once -#include "base/scoped_ptr.h" #include "base/timer.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" @@ -37,18 +37,23 @@ class WebPageScreen : public TabContentsDelegate { const gfx::Rect& initial_pos, bool user_gesture) {} virtual void ActivateContents(TabContents* contents) {} + virtual void DeactivateContents(TabContents* contents) {} virtual void LoadingStateChanged(TabContents* source) = 0; virtual void CloseContents(TabContents* source) {} virtual bool IsPopup(TabContents* source) { return false; } virtual void URLStarredChanged(TabContents* source, bool starred) {} virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} - virtual bool ShouldAddNavigationToHistory() const { return false; } + virtual bool ShouldAddNavigationToHistory( + const history::HistoryAddPageArgs& add_page_args, + NavigationType::Type navigation_type) { + return false; + } virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} virtual bool HandleContextMenu(const ContextMenuParams& params); // Called by |timeout_timer_|. Stops page fetching and closes screen. - void OnNetworkTimeout(); + virtual void OnNetworkTimeout(); // Start/stop timeout timer. void StartTimeoutTimer(); diff --git a/chrome/browser/chromeos/login/web_page_view.cc b/chrome/browser/chromeos/login/web_page_view.cc index daac923..5205fa1 100644 --- a/chrome/browser/chromeos/login/web_page_view.cc +++ b/chrome/browser/chromeos/login/web_page_view.cc @@ -7,9 +7,9 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/callback.h" +#include "base/logging.h" #include "base/string_util.h" #include "base/time.h" -#include "base/values.h" #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/rounded_rect_painter.h" @@ -53,7 +53,7 @@ WizardWebPageViewTabContents::WizardWebPageViewTabContents( Profile* profile, SiteInstance* site_instance, WebPageDelegate* page_delegate) - : TabContents(profile, site_instance, MSG_ROUTING_NONE, NULL), + : TabContents(profile, site_instance, MSG_ROUTING_NONE, NULL, NULL), page_delegate_(page_delegate) { } @@ -63,15 +63,18 @@ void WizardWebPageViewTabContents::DidFailProvisionalLoadWithError( int error_code, const GURL& url, bool showing_repost_interstitial) { + LOG(ERROR) << "Page load failed. URL = " << url << ", error: " << error_code; page_delegate_->OnPageLoadFailed(url.spec()); } void WizardWebPageViewTabContents::DidDisplayInsecureContent() { - page_delegate_->OnPageLoadFailed(""); + LOG(ERROR) << "Page load failed: did display insecure content"; + page_delegate_->OnPageLoadFailed("Displayed insecure content"); } void WizardWebPageViewTabContents::DidRunInsecureContent( const std::string& security_origin) { + LOG(ERROR) << "Page load failed: did run insecure content"; page_delegate_->OnPageLoadFailed(security_origin); } @@ -80,6 +83,7 @@ void WizardWebPageViewTabContents::DocumentLoadedInFrame() { } void WizardWebPageViewTabContents::OnContentBlocked(ContentSettingsType type) { + LOG(ERROR) << "Page load failed: content blocked. Type: " << type; page_delegate_->OnPageLoadFailed(""); } diff --git a/chrome/browser/chromeos/login/web_page_view.h b/chrome/browser/chromeos/login/web_page_view.h index 8197743..b497715 100644 --- a/chrome/browser/chromeos/login/web_page_view.h +++ b/chrome/browser/chromeos/login/web_page_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WEB_PAGE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_WEB_PAGE_VIEW_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 8506921..fa0f938 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc @@ -11,20 +11,25 @@ #include <string> #include <vector> -#include "app/l10n_util.h" +#include "app/resource_bundle.h" #include "base/command_line.h" +#include "base/file_util.h" #include "base/logging.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/cros/system_library.h" #include "chrome/browser/chromeos/customization_document.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" +#include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/login/account_screen.h" +#include "chrome/browser/chromeos/login/apply_services_customization.h" #include "chrome/browser/chromeos/login/background_view.h" #include "chrome/browser/chromeos/login/eula_view.h" #include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/html_page_screen.h" #include "chrome/browser/chromeos/login/language_switch_menu.h" #include "chrome/browser/chromeos/login/login_screen.h" #include "chrome/browser/chromeos/login/login_utils.h" @@ -34,11 +39,13 @@ #include "chrome/browser/chromeos/login/update_screen.h" #include "chrome/browser/chromeos/login/user_image_screen.h" #include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "chrome/browser/chromeos/wm_ipc.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" #include "cros/chromeos_wm_ipc_enums.h" #include "unicode/timezone.h" #include "views/accelerator.h" @@ -49,39 +56,54 @@ namespace { // A boolean pref of the OOBE complete flag. -const wchar_t kOobeComplete[] = L"OobeComplete"; +const char kOobeComplete[] = "OobeComplete"; -// Default size of the OOBE screen. -const int kWizardScreenWidth = 700; -const int kWizardScreenHeight = 416; +// Path to OEM partner startup customization manifest. +const char kStartupCustomizationManifestPath[] = + "/mnt/partner_partition/etc/chromeos/startup_manifest.json"; + +// Path to flag file indicating that OOBE was completed successfully. +const char kOobeCompleteFlagFilePath[] = + "/home/chronos/.oobe_completed"; + +// Upadate window will be behind the curtain at most |kMaximalCurtainTimeSec|. +const int kMaximalCurtainTimeSec = 15; + +// Time in seconds that we wait for the device to reboot. +// If reboot didn't happen, ask user to reboot device manually. +const int kWaitForRebootTimeSec = 3; // RootView of the Widget WizardController creates. Contains the contents of the // WizardController. class ContentView : public views::View { public: - ContentView(int window_x, int window_y, int screen_w, int screen_h) - : window_x_(window_x), - window_y_(window_y), - screen_w_(screen_w), - screen_h_(screen_h), - accel_account_screen_(views::Accelerator(base::VKEY_A, - false, true, true)), - accel_login_screen_(views::Accelerator(base::VKEY_L, - false, true, true)), - accel_network_screen_(views::Accelerator(base::VKEY_N, - false, true, true)), - accel_update_screen_(views::Accelerator(base::VKEY_U, - false, true, true)), - accel_image_screen_(views::Accelerator(base::VKEY_I, - false, true, true)), - accel_eula_screen_(views::Accelerator(base::VKEY_E, - false, true, true)) { + ContentView() + : accel_enable_accessibility_( + WizardAccessibilityHelper::GetAccelerator()) { + AddAccelerator(accel_enable_accessibility_); +#if !defined(OFFICIAL_BUILD) + accel_account_screen_ = views::Accelerator(app::VKEY_A, + false, true, true); + accel_login_screen_ = views::Accelerator(app::VKEY_L, + false, true, true); + accel_network_screen_ = views::Accelerator(app::VKEY_N, + false, true, true); + accel_update_screen_ = views::Accelerator(app::VKEY_U, + false, true, true); + accel_image_screen_ = views::Accelerator(app::VKEY_I, + false, true, true); + accel_eula_screen_ = views::Accelerator(app::VKEY_E, + false, true, true); + accel_register_screen_ = views::Accelerator(app::VKEY_R, + false, true, true); AddAccelerator(accel_account_screen_); AddAccelerator(accel_login_screen_); AddAccelerator(accel_network_screen_); AddAccelerator(accel_update_screen_); AddAccelerator(accel_image_screen_); AddAccelerator(accel_eula_screen_); + AddAccelerator(accel_register_screen_); +#endif } ~ContentView() { @@ -96,7 +118,11 @@ class ContentView : public views::View { if (!controller) return false; - if (accel == accel_account_screen_) { + if (accel == accel_enable_accessibility_) { + WizardAccessibilityHelper::GetInstance()->EnableAccessibility( + controller->contents()); +#if !defined(OFFICIAL_BUILD) + } else if (accel == accel_account_screen_) { controller->ShowAccountScreen(); } else if (accel == accel_login_screen_) { controller->ShowLoginScreen(); @@ -108,6 +134,9 @@ class ContentView : public views::View { controller->ShowUserImageScreen(); } else if (accel == accel_eula_screen_) { controller->ShowEulaScreen(); + } else if (accel == accel_register_screen_) { + controller->ShowRegistrationScreen(); +#endif } else { return false; } @@ -115,16 +144,6 @@ class ContentView : public views::View { return true; } - void PaintBackground(gfx::Canvas* canvas) { - if (painter_.get()) { - // TODO(sky): nuke this once new login manager is in place. This needs to - // exist because with no window manager transparency isn't really - // supported. - canvas->TranslateInt(-window_x_, -window_y_); - painter_->Paint(screen_w_, screen_h_, canvas); - } - } - virtual void Layout() { for (int i = 0; i < GetChildViewCount(); ++i) { views::View* cur = GetChildViewAt(i); @@ -136,17 +155,16 @@ class ContentView : public views::View { private: scoped_ptr<views::Painter> painter_; - const int window_x_; - const int window_y_; - const int screen_w_; - const int screen_h_; - +#if !defined(OFFICIAL_BUILD) views::Accelerator accel_account_screen_; views::Accelerator accel_login_screen_; views::Accelerator accel_network_screen_; views::Accelerator accel_update_screen_; views::Accelerator accel_image_screen_; views::Accelerator accel_eula_screen_; + views::Accelerator accel_register_screen_; +#endif + views::Accelerator accel_enable_accessibility_; DISALLOW_COPY_AND_ASSIGN(ContentView); }; @@ -162,6 +180,26 @@ void DeleteWizardControllerAndLaunchBrowser(WizardController* controller) { ProfileManager::GetDefaultProfile())); } +const chromeos::StartupCustomizationDocument* LoadStartupManifest() { + // Load partner customization startup manifest if it is available. + FilePath startup_manifest_path(kStartupCustomizationManifestPath); + if (file_util::PathExists(startup_manifest_path)) { + scoped_ptr<chromeos::StartupCustomizationDocument> customization( + new chromeos::StartupCustomizationDocument()); + bool manifest_loaded = customization->LoadManifestFromFile( + startup_manifest_path); + if (manifest_loaded) { + LOG(INFO) << "Startup manifest loaded successfully"; + return customization.release(); + } else { + LOG(ERROR) << "Error loading startup manifest. " << + kStartupCustomizationManifestPath; + } + } + + return NULL; +} + } // namespace const char WizardController::kNetworkScreenName[] = "network"; @@ -171,6 +209,7 @@ const char WizardController::kUpdateScreenName[] = "update"; const char WizardController::kUserImageScreenName[] = "image"; const char WizardController::kEulaScreenName[] = "eula"; const char WizardController::kRegistrationScreenName[] = "register"; +const char WizardController::kHTMLPageScreenName[] = "html"; // Passing this parameter as a "first screen" initiates full OOBE flow. const char WizardController::kOutOfBoxScreenName[] = "oobe"; @@ -191,6 +230,11 @@ WizardController::WizardController() background_view_(NULL), contents_(NULL), current_screen_(NULL), +#if defined(OFFICIAL_BUILD) + is_official_build_(true), +#else + is_official_build_(false), +#endif is_out_of_box_(false), is_test_mode_(false), observer_(NULL) { @@ -211,56 +255,43 @@ WizardController::~WizardController() { void WizardController::Init(const std::string& first_screen_name, const gfx::Rect& screen_bounds) { + LOG(INFO) << "Starting OOBE wizard with screen: " << first_screen_name; DCHECK(!contents_); + first_screen_name_ = first_screen_name; - int offset_x = (screen_bounds.width() - kWizardScreenWidth) / 2; - int offset_y = (screen_bounds.height() - kWizardScreenHeight) / 2; - int window_x = screen_bounds.x() + offset_x; - int window_y = screen_bounds.y() + offset_y; + // When device is not registered yet we need to load startup manifest as well. + // In case of OOBE (network-EULA-update) manifest has been loaded in + // ShowLoginWizard(). + if (IsOobeCompleted() && !IsDeviceRegistered()) + SetCustomization(LoadStartupManifest()); - contents_ = new ContentView(offset_x, offset_y, - screen_bounds.width(), screen_bounds.height()); - - views::WidgetGtk* window = - new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); - widget_ = window; - window->MakeTransparent(); - // Window transparency makes background flicker through controls that - // are constantly updating its contents (like image view with video - // stream). Hence enabling double buffer. - window->EnableDoubleBuffer(true); - window->Init(NULL, gfx::Rect(window_x, window_y, kWizardScreenWidth, - kWizardScreenHeight)); - chromeos::WmIpc::instance()->SetWindowType( - window->GetNativeView(), - chromeos::WM_IPC_WINDOW_LOGIN_GUEST, - NULL); - window->SetContentsView(contents_); - - PrefService* prefs = g_browser_process->local_state(); - bool oobe_complete = prefs->GetBoolean(kOobeComplete); + screen_bounds_ = screen_bounds; + contents_ = new ContentView(); + bool oobe_complete = IsOobeCompleted(); if (!oobe_complete || first_screen_name == kOutOfBoxScreenName) { is_out_of_box_ = true; } ShowFirstScreen(first_screen_name); - - // This keeps the window from flashing at startup. - GdkWindow* gdk_window = window->GetNativeView()->window; - gdk_window_set_back_pixmap(gdk_window, NULL, false); } void WizardController::Show() { - DCHECK(widget_); - widget_->Show(); + // In tests we might startup without initial screen + // so widget_ hasn't been created yet. + if (first_screen_name_ != kTestNoScreenName) + DCHECK(widget_); + if (widget_) + widget_->Show(); } void WizardController::ShowBackground(const gfx::Rect& bounds) { DCHECK(!background_widget_); background_widget_ = chromeos::BackgroundView::CreateWindowContainingView(bounds, + GURL(), &background_view_); + background_view_->SetOobeProgressBarVisible(true); background_widget_->Show(); } @@ -270,11 +301,12 @@ void WizardController::OwnBackground( DCHECK(!background_widget_); background_widget_ = background_widget; background_view_ = background_view; + background_view_->OnOwnerChanged(); } chromeos::NetworkScreen* WizardController::GetNetworkScreen() { if (!network_screen_.get()) - network_screen_.reset(new chromeos::NetworkScreen(this, is_out_of_box_)); + network_screen_.reset(new chromeos::NetworkScreen(this)); return network_screen_.get(); } @@ -291,8 +323,11 @@ chromeos::AccountScreen* WizardController::GetAccountScreen() { } chromeos::UpdateScreen* WizardController::GetUpdateScreen() { - if (!update_screen_.get()) + if (!update_screen_.get()) { update_screen_.reset(new chromeos::UpdateScreen(this)); + update_screen_->SetMaximalCurtainTime(kMaximalCurtainTimeSec); + update_screen_->SetRebootCheckDelay(kWaitForRebootTimeSec); + } return update_screen_.get(); } @@ -314,13 +349,35 @@ chromeos::RegistrationScreen* WizardController::GetRegistrationScreen() { return registration_screen_.get(); } +chromeos::HTMLPageScreen* WizardController::GetHTMLPageScreen() { + if (!html_page_screen_.get()) { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + std::string url; + // It's strange but args may contains empty strings. + for (size_t i = 0; i < command_line->args().size(); i++) { + if (!command_line->args()[i].empty()) { + DCHECK(url.empty()) << "More than one URL in command line"; + url = command_line->args()[i]; + } + } + DCHECK(!url.empty()) << "No URL in commane line"; + html_page_screen_.reset(new chromeos::HTMLPageScreen(this, url)); + } + return html_page_screen_.get(); +} + void WizardController::ShowNetworkScreen() { SetStatusAreaVisible(false); SetCurrentScreen(GetNetworkScreen()); + background_view_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK); } -void WizardController::ShowLoginScreen() { +chromeos::ExistingUserController* WizardController::ShowLoginScreen() { SetStatusAreaVisible(true); + background_view_->SetOobeProgress(chromeos::BackgroundView::SIGNIN); + + // Initiate services customization. + chromeos::ApplyServicesCustomization::StartIfNeeded(); // When run under automation test show plain login screen. if (!is_test_mode_ && @@ -338,44 +395,71 @@ void WizardController::ShowLoginScreen() { controller->Init(); background_widget_ = NULL; background_view_ = NULL; + MessageLoop::current()->DeleteSoon(FROM_HERE, this); - return; + + return controller; } SetCurrentScreen(GetLoginScreen()); + return NULL; } void WizardController::ShowAccountScreen() { + LOG(INFO) << "Showing create account screen."; SetStatusAreaVisible(true); SetCurrentScreen(GetAccountScreen()); } void WizardController::ShowUpdateScreen() { + LOG(INFO) << "Showing update screen."; SetStatusAreaVisible(true); SetCurrentScreen(GetUpdateScreen()); + // There is no special step for update. +#if defined(OFFICIAL_BUILD) + background_view_->SetOobeProgress(chromeos::BackgroundView::EULA); +#else + background_view_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK); +#endif } void WizardController::ShowUserImageScreen() { - SetStatusAreaVisible(true); + LOG(INFO) << "Showing user image screen."; + SetStatusAreaVisible(false); SetCurrentScreen(GetUserImageScreen()); + background_view_->SetOobeProgress(chromeos::BackgroundView::PICTURE); } void WizardController::ShowEulaScreen() { + LOG(INFO) << "Showing EULA screen."; SetStatusAreaVisible(false); SetCurrentScreen(GetEulaScreen()); +#if defined(OFFICIAL_BUILD) + background_view_->SetOobeProgress(chromeos::BackgroundView::EULA); +#endif } void WizardController::ShowRegistrationScreen() { + if (!GetCustomization() || + !GURL(GetCustomization()->registration_url()).is_valid()) { + LOG(INFO) << + "Skipping registration screen: manifest not defined or invalid URL."; + OnRegistrationSkipped(); + return; + } + LOG(INFO) << "Showing registration screen."; SetStatusAreaVisible(true); SetCurrentScreen(GetRegistrationScreen()); +#if defined(OFFICIAL_BUILD) + background_view_->SetOobeProgress(chromeos::BackgroundView::REGISTRATION); +#endif } -void WizardController::SetStatusAreaVisible(bool visible) { - // When ExistingUserController passes background ownership - // to WizardController it happens after screen is shown. - if (background_view_) { - background_view_->SetStatusAreaVisible(visible); - } +void WizardController::ShowHTMLPageScreen() { + LOG(INFO) << "Showing HTML page screen."; + SetStatusAreaVisible(true); + background_view_->SetOobeProgressBarVisible(false); + SetCurrentScreen(GetHTMLPageScreen()); } void WizardController::SetCustomization( @@ -384,13 +468,25 @@ void WizardController::SetCustomization( } const chromeos::StartupCustomizationDocument* - WizardController::GetCustomization() { + WizardController::GetCustomization() const { return customization_.get(); } +void WizardController::SkipRegistration() { + if (current_screen_ == GetRegistrationScreen()) + OnRegistrationSkipped(); + else + LOG(ERROR) << "Registration screen is not active."; +} + // static void WizardController::RegisterPrefs(PrefService* local_state) { local_state->RegisterBooleanPref(kOobeComplete, false); + // Check if the pref is already registered in case + // Preferences::RegisterUserPrefs runs before this code in the future. + if (local_state->FindPreference(prefs::kAccessibilityEnabled) == NULL) { + local_state->RegisterBooleanPref(prefs::kAccessibilityEnabled, false); + } } /////////////////////////////////////////////////////////////////////////////// @@ -416,12 +512,11 @@ void WizardController::OnLoginCreateAccount() { } void WizardController::OnNetworkConnected() { - if (is_out_of_box_) { + if (is_official_build_) { + ShowEulaScreen(); + } else { ShowUpdateScreen(); GetUpdateScreen()->StartUpdate(); - } else { - // TODO(nkostylev): Remove this path after accelerator is removed. - ShowLoginScreen(); } } @@ -432,22 +527,20 @@ void WizardController::OnNetworkOffline() { } void WizardController::OnAccountCreateBack() { - ShowLoginScreen(); + chromeos::ExistingUserController* controller = ShowLoginScreen(); + if (controller) + controller->SelectNewUser(); } void WizardController::OnAccountCreated() { - ShowLoginScreen(); - chromeos::LoginScreen* login = GetLoginScreen(); - if (!username_.empty()) { - login->view()->SetUsername(username_); - if (!password_.empty()) { - login->view()->SetPassword(password_); - // TODO(dpolukhin): clear password memory for real. Now it is not - // a problem becuase we can't extract password from the form. - password_.clear(); - login->view()->Login(); - } - } + chromeos::ExistingUserController* controller = ShowLoginScreen(); + if (controller) + controller->LoginNewUser(username_, password_); + else + Login(username_, password_); + // TODO(dpolukhin): clear password memory for real. Now it is not + // a problem because we can't extract password from the form. + password_.clear(); } void WizardController::OnConnectionFailed() { @@ -456,12 +549,12 @@ void WizardController::OnConnectionFailed() { } void WizardController::OnUpdateCompleted() { - ShowEulaScreen(); + OnOOBECompleted(); } void WizardController::OnEulaAccepted() { - MarkOobeCompleted(); - ShowLoginScreen(); + ShowUpdateScreen(); + GetUpdateScreen()->StartUpdate(); } void WizardController::OnUpdateErrorCheckingForUpdate() { @@ -470,7 +563,7 @@ void WizardController::OnUpdateErrorCheckingForUpdate() { // screen if there is any error checking for an update. // They could use "browse without sign-in" feature to set up the network to be // able to perform the update later. - ShowEulaScreen(); + OnOOBECompleted(); } void WizardController::OnUpdateErrorUpdating() { @@ -479,7 +572,7 @@ void WizardController::OnUpdateErrorUpdating() { // TODO(nkostylev): Show message to the user explaining update error. // TODO(nkostylev): Update should be required during OOBE. // Temporary fix, need to migrate to new API. http://crosbug.com/4321 - ShowEulaScreen(); + OnOOBECompleted(); } void WizardController::OnUserImageSelected() { @@ -499,8 +592,12 @@ void WizardController::OnUserImageSkipped() { } void WizardController::OnRegistrationSuccess() { - // TODO(nkostylev): Registration screen should be shown on first sign in. - ShowLoginScreen(); + MarkDeviceRegistered(); + if (chromeos::UserManager::Get()->logged_in_user().email().empty()) { + chromeos::LoginUtils::Get()->CompleteOffTheRecordLogin(start_url_); + } else { + ShowUserImageScreen(); + } } void WizardController::OnRegistrationSkipped() { @@ -508,26 +605,92 @@ void WizardController::OnRegistrationSkipped() { OnRegistrationSuccess(); } +void WizardController::OnOOBECompleted() { + MarkOobeCompleted(); + ShowLoginScreen(); +} + /////////////////////////////////////////////////////////////////////////////// // WizardController, private: +views::WidgetGtk* WizardController::CreateScreenWindow( + const gfx::Rect& bounds, bool initial_show) { + views::WidgetGtk* window = + new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); + widget_ = window; + window->MakeTransparent(); + // Window transparency makes background flicker through controls that + // are constantly updating its contents (like image view with video + // stream). Hence enabling double buffer. + window->EnableDoubleBuffer(true); + window->Init(NULL, bounds); + std::vector<int> params; + // For initial show WM would animate background window. + // Otherwise it stays unchaged. + params.push_back(initial_show); + chromeos::WmIpc::instance()->SetWindowType( + window->GetNativeView(), + chromeos::WM_IPC_WINDOW_LOGIN_GUEST, + ¶ms); + window->SetContentsView(contents_); + return window; +} + +gfx::Rect WizardController::GetWizardScreenBounds(int screen_width, + int screen_height) const { + int offset_x = (screen_bounds_.width() - screen_width) / 2; + int offset_y = (screen_bounds_.height() - screen_height) / 2; + int window_x = screen_bounds_.x() + offset_x; + int window_y = screen_bounds_.y() + offset_y; + return gfx::Rect(window_x, window_y, screen_width, screen_height); +} + + void WizardController::SetCurrentScreen(WizardScreen* new_current) { - if (current_screen_ == new_current) + if (current_screen_ == new_current || + new_current == NULL) return; - if (current_screen_) + + bool initial_show = true; + if (current_screen_) { + initial_show = false; current_screen_->Hide(); + } + current_screen_ = new_current; - if (current_screen_) { - current_screen_->Show(); - contents_->Layout(); + bool force_widget_show = false; + views::WidgetGtk* window = NULL; + + gfx::Rect current_bounds; + if (widget_) + widget_->GetBounds(¤t_bounds, false); + gfx::Size new_screen_size = current_screen_->GetScreenSize(); + gfx::Rect new_bounds = GetWizardScreenBounds(new_screen_size.width(), + new_screen_size.height()); + if (new_bounds != current_bounds) { + if (widget_) + widget_->Close(); + force_widget_show = true; + window = CreateScreenWindow(new_bounds, initial_show); } + current_screen_->Show(); + contents_->Layout(); contents_->SchedulePaint(); + if (force_widget_show) { + // This keeps the window from flashing at startup. + GdkWindow* gdk_window = window->GetNativeView()->window; + gdk_window_set_back_pixmap(gdk_window, NULL, false); + if (widget_) + widget_->Show(); + } } -void WizardController::OnSetUserNamePassword(const std::string& username, - const std::string& password) { - username_ = username; - password_ = password; +void WizardController::SetStatusAreaVisible(bool visible) { + // When ExistingUserController passes background ownership + // to WizardController it happens after screen is shown. + if (background_view_) { + background_view_->SetStatusAreaVisible(visible); + } } void WizardController::ShowFirstScreen(const std::string& first_screen_name) { @@ -547,7 +710,14 @@ void WizardController::ShowFirstScreen(const std::string& first_screen_name) { } else if (first_screen_name == kEulaScreenName) { ShowEulaScreen(); } else if (first_screen_name == kRegistrationScreenName) { - ShowRegistrationScreen(); + if (is_official_build_) { + ShowRegistrationScreen(); + } else { + // Just proceed to image screen. + OnRegistrationSuccess(); + } + } else if (first_screen_name == kHTMLPageScreenName) { + ShowHTMLPageScreen(); } else if (first_screen_name != kTestNoScreenName) { if (is_out_of_box_) { ShowNetworkScreen(); @@ -557,6 +727,25 @@ void WizardController::ShowFirstScreen(const std::string& first_screen_name) { } } +void WizardController::Login(const std::string& username, + const std::string& password) { + chromeos::LoginScreen* login = GetLoginScreen(); + if (username.empty()) + return; + login->view()->SetUsername(username); + + if (password.empty()) + return; + login->view()->SetPassword(password); + login->view()->Login(); +} + +// static +bool WizardController::IsOobeCompleted() { + return g_browser_process->local_state()->GetBoolean(kOobeComplete); +} + +// static void WizardController::MarkOobeCompleted() { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(kOobeComplete, true); @@ -564,9 +753,26 @@ void WizardController::MarkOobeCompleted() { prefs->SavePersistentPrefs(); } +// static +bool WizardController::IsDeviceRegistered() { + FilePath oobe_complete_flag_file_path(kOobeCompleteFlagFilePath); + return file_util::PathExists(oobe_complete_flag_file_path); +} + +// static +void WizardController::MarkDeviceRegistered() { + // Create flag file for boot-time init scripts. + FilePath oobe_complete_path(kOobeCompleteFlagFilePath); + FILE* oobe_flag_file = file_util::OpenFile(oobe_complete_path, "w+b"); + DCHECK(oobe_flag_file != NULL) << kOobeCompleteFlagFilePath; + if (oobe_flag_file != NULL) + file_util::CloseFile(oobe_flag_file); +} + /////////////////////////////////////////////////////////////////////////////// // WizardController, chromeos::ScreenObserver overrides: void WizardController::OnExit(ExitCodes exit_code) { + LOG(INFO) << "Wizard screen exit code: " << exit_code; switch (exit_code) { case LOGIN_SIGN_IN_SELECTED: OnLoginSignInSelected(); @@ -611,6 +817,9 @@ void WizardController::OnExit(ExitCodes exit_code) { case EULA_ACCEPTED: OnEulaAccepted(); break; + case EULA_BACK: + ShowNetworkScreen(); + break; case REGISTRATION_SUCCESS: OnRegistrationSuccess(); break; @@ -622,6 +831,12 @@ void WizardController::OnExit(ExitCodes exit_code) { } } +void WizardController::OnSetUserNamePassword(const std::string& username, + const std::string& password) { + username_ = username; + password_ = password; +} + /////////////////////////////////////////////////////////////////////////////// // WizardController, WizardScreen overrides: views::View* WizardController::GetWizardView() { @@ -639,6 +854,10 @@ void ShowLoginWizard(const std::string& first_screen_name, const gfx::Size& size) { LOG(INFO) << "showing login screen: " << first_screen_name; + // The login screen will enable alternate keyboard layouts, but we don't want + // to start the IME process unless one is selected. + chromeos::CrosLibrary::Get()->GetInputMethodLibrary()-> + SetDeferImeStartup(true); // Tell the window manager that the user isn't logged in. chromeos::WmIpc::instance()->SetLoggedInProperty(false); @@ -648,7 +867,7 @@ void ShowLoginWizard(const std::string& first_screen_name, const std::string locale = g_browser_process->GetApplicationLocale(); const std::string initial_input_method_id = g_browser_process->local_state()->GetString( - chromeos::kPreferredKeyboardLayout); + chromeos::language_prefs::kPreferredKeyboardLayout); chromeos::input_method::EnableInputMethods( locale, chromeos::input_method::kKeyboardLayoutsOnly, initial_input_method_id); @@ -657,8 +876,7 @@ void ShowLoginWizard(const std::string& first_screen_name, gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(size)); // Check whether we need to execute OOBE process. - bool oobe_complete = g_browser_process->local_state()-> - GetBoolean(kOobeComplete); + bool oobe_complete = WizardController::IsOobeCompleted(); if (first_screen_name.empty() && oobe_complete && @@ -667,52 +885,73 @@ void ShowLoginWizard(const std::string& first_screen_name, switches::kEnableLoginImages)) { std::vector<chromeos::UserManager::User> users = chromeos::UserManager::Get()->GetUsers(); + + // Fix for users who updated device and thus never passed register screen. + // If we already have user we assume that it is not a second part of OOBE. + // See http://crosbug.com/6289 + if (!WizardController::IsDeviceRegistered() && !users.empty()) { + LOG(INFO) << "Mark device registered because there are remembered users: " + << users.size(); + WizardController::MarkDeviceRegistered(); + } + // ExistingUserController deletes itself. (new chromeos::ExistingUserController(users, screen_bounds))->Init(); + + // Initiate services customization. + chromeos::ApplyServicesCustomization::StartIfNeeded(); + return; } - scoped_ptr<chromeos::StartupCustomizationDocument> customization; - - if (!oobe_complete) { - // Load partner customization startup manifest if needed. - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kStartupManifest)) { - customization.reset(new chromeos::StartupCustomizationDocument()); - FilePath manifest_path = - CommandLine::ForCurrentProcess()->GetSwitchValuePath( - switches::kStartupManifest); - bool manifest_loaded = customization->LoadManifestFromFile(manifest_path); - DCHECK(manifest_loaded) << manifest_path.value(); - } + // Create and show the wizard. + WizardController* controller = new WizardController(); - // Do UX customizations if needed. - if (customization != NULL) { - // Switch to initial locale if specified by customization. - const std::string locale = customization->initial_locale(); - if (!locale.empty()) { - chromeos::LanguageSwitchMenu::SwitchLanguage(locale); - } + // Load startup manifest. + controller->SetCustomization(LoadStartupManifest()); - // Set initial timezone if specified by customization. - const std::string timezone_name = customization->initial_timezone(); - if (!timezone_name.empty()) { - icu::TimeZone* timezone = icu::TimeZone::createTimeZone( - icu::UnicodeString::fromUTF8(timezone_name)); - chromeos::CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone); + std::string locale; + if (controller->GetCustomization()) { + // Switch to initial locale if specified by customization + // and has not been set yet. We cannot call + // chromeos::LanguageSwitchMenu::SwitchLanguage here before + // EmitLoginPromptReady. + const std::string current_locale = + g_browser_process->local_state()->GetString( + prefs::kApplicationLocale); + LOG(INFO) << "current locale: " << current_locale; + if (current_locale.empty()) { + locale = controller->GetCustomization()->initial_locale(); + LOG(INFO) << "initial locale: " << locale; + if (!locale.empty()) { + ResourceBundle::ReloadSharedInstance(locale); } } } - // Create and show the wizard. - WizardController* controller = new WizardController(); - if (!oobe_complete) - controller->SetCustomization(customization.release()); controller->ShowBackground(screen_bounds); controller->Init(first_screen_name, screen_bounds); controller->Show(); + if (chromeos::CrosLibrary::Get()->EnsureLoaded()) chromeos::CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady(); + + if (controller->GetCustomization()) { + if (!locale.empty()) + chromeos::LanguageSwitchMenu::SwitchLanguage(locale); + + // Set initial timezone if specified by customization. + const std::string timezone_name = + controller->GetCustomization()->initial_timezone(); + LOG(INFO) << "initial time zone: " << timezone_name; + // Apply locale customizations only once so preserve whatever locale + // user has changed to during OOBE. + if (!timezone_name.empty()) { + icu::TimeZone* timezone = icu::TimeZone::createTimeZone( + icu::UnicodeString::fromUTF8(timezone_name)); + chromeos::CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone); + } + } } } // namespace browser diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h index 25394ff..44fb432 100644 --- a/chrome/browser/chromeos/login/wizard_controller.h +++ b/chrome/browser/chromeos/login/wizard_controller.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_CONTROLLER_H_ +#pragma once #include <string> @@ -12,6 +13,8 @@ #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/chromeos/login/view_screen.h" #include "chrome/browser/chromeos/login/wizard_screen.h" +#include "gfx/rect.h" +#include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest_prod.h" class PrefService; @@ -22,6 +25,8 @@ namespace chromeos { class AccountScreen; class BackgroundView; class EulaScreen; +class ExistingUserController; +class HTMLPageScreen; class LoginScreen; class NetworkScreen; class RegistrationScreen; @@ -37,6 +42,7 @@ class Rect; namespace views { class Views; class Widget; +class WidgetGtk; } // Class that manages control flow between wizard screens. Wizard controller @@ -52,6 +58,18 @@ class WizardController : public chromeos::ScreenObserver, return default_controller_; } + // Returns OOBE completion status. + static bool IsOobeCompleted(); + + // Marks OOBE process as completed. + static void MarkOobeCompleted(); + + // Returns device registration completion status, i.e. second part of OOBE. + static bool IsDeviceRegistered(); + + // Marks device registered. i.e. second part of OOBE is completed. + static void MarkDeviceRegistered(); + // Shows the first screen defined by |first_screen_name| or by default // if the parameter is empty. |screen_bounds| are used to calculate position // of the wizard screen. @@ -79,33 +97,41 @@ class WizardController : public chromeos::ScreenObserver, chromeos::UserImageScreen* GetUserImageScreen(); chromeos::EulaScreen* GetEulaScreen(); chromeos::RegistrationScreen* GetRegistrationScreen(); + chromeos::HTMLPageScreen* GetHTMLPageScreen(); // Show specific screen. void ShowNetworkScreen(); - void ShowLoginScreen(); void ShowAccountScreen(); void ShowUpdateScreen(); void ShowUserImageScreen(); void ShowEulaScreen(); void ShowRegistrationScreen(); + void ShowHTMLPageScreen(); + // Shows the default login screen and returns NULL or shows images login + // screen and returns the corresponding controller instance for optional + // tweaking. + chromeos::ExistingUserController* ShowLoginScreen(); // Returns a pointer to the current screen or NULL if there's no such // screen. WizardScreen* current_screen() const { return current_screen_; } - // True if WizardController is in OOBE mode. - bool is_oobe() { return is_out_of_box_; } - // Overrides observer for testing. void set_observer(ScreenObserver* observer) { observer_ = observer; } + // Set URL to open on browser launch. + void set_start_url(const GURL& start_url) { start_url_ = start_url; } + // Sets partner startup customization. WizardController takes ownership // of the document object. void SetCustomization( const chromeos::StartupCustomizationDocument* customization); // Returns partner startup customization document owned by WizardController. - const chromeos::StartupCustomizationDocument* GetCustomization(); + const chromeos::StartupCustomizationDocument* GetCustomization() const; + + // If being at register screen proceeds to the next one. + void SkipRegistration(); // Registers OOBE preferences. static void RegisterPrefs(PrefService* local_state); @@ -119,6 +145,7 @@ class WizardController : public chromeos::ScreenObserver, static const char kOutOfBoxScreenName[]; static const char kTestNoScreenName[]; static const char kEulaScreenName[]; + static const char kHTMLPageScreenName[]; private: // Exit handlers: @@ -138,6 +165,17 @@ class WizardController : public chromeos::ScreenObserver, void OnUserImageSkipped(); void OnRegistrationSuccess(); void OnRegistrationSkipped(); + void OnOOBECompleted(); + + // Creates wizard screen window with the specified |bounds|. + // If |initial_show| initial animation (window & background) is shown. + // Otherwise only window is animated. + views::WidgetGtk* CreateScreenWindow(const gfx::Rect& bounds, + bool initial_show); + + // Returns bounds for the wizard screen host window in screen coordinates. + // Calculates bounds using screen_bounds_. + gfx::Rect GetWizardScreenBounds(int screen_width, int screen_height) const; // Switches from one screen to another. void SetCurrentScreen(WizardScreen* screen); @@ -158,8 +196,8 @@ class WizardController : public chromeos::ScreenObserver, // sets it as the current one. void ShowFirstScreen(const std::string& first_screen_name); - // Marks OOBE process as completed. - void MarkOobeCompleted(); + // Logs in the specified user via default login screen. + void Login(const std::string& username, const std::string& password); // Widget we're showing in. views::Widget* widget_; @@ -171,6 +209,9 @@ class WizardController : public chromeos::ScreenObserver, // Contents view. views::View* contents_; + // Used to calculate position of the wizard screen. + gfx::Rect screen_bounds_; + // Screens. scoped_ptr<chromeos::NetworkScreen> network_screen_; scoped_ptr<chromeos::LoginScreen> login_screen_; @@ -179,6 +220,7 @@ class WizardController : public chromeos::ScreenObserver, scoped_ptr<chromeos::UserImageScreen> user_image_screen_; scoped_ptr<chromeos::EulaScreen> eula_screen_; scoped_ptr<chromeos::RegistrationScreen> registration_screen_; + scoped_ptr<chromeos::HTMLPageScreen> html_page_screen_; // Screen that's currently active. WizardScreen* current_screen_; @@ -186,6 +228,9 @@ class WizardController : public chromeos::ScreenObserver, std::string username_; std::string password_; + // True if running official BUILD. + bool is_official_build_; + // True if full OOBE flow should be shown. bool is_out_of_box_; @@ -193,6 +238,9 @@ class WizardController : public chromeos::ScreenObserver, // login screen. bool is_test_mode_; + // Value of the screen name that WizardController was started with. + std::string first_screen_name_; + // NULL by default - controller itself is observer. Mock could be assigned. ScreenObserver* observer_; @@ -202,8 +250,12 @@ class WizardController : public chromeos::ScreenObserver, // Partner startup customizations. scoped_ptr<const chromeos::StartupCustomizationDocument> customization_; + // URL to open on browser launch. + GURL start_url_; + FRIEND_TEST_ALL_PREFIXES(WizardControllerFlowTest, ControlFlowErrorNetwork); FRIEND_TEST_ALL_PREFIXES(WizardControllerFlowTest, ControlFlowErrorUpdate); + FRIEND_TEST_ALL_PREFIXES(WizardControllerFlowTest, ControlFlowEulaDeclined); FRIEND_TEST_ALL_PREFIXES(WizardControllerFlowTest, ControlFlowLanguageOnLogin); FRIEND_TEST_ALL_PREFIXES(WizardControllerFlowTest, diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index 6459286..e38fdda 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc @@ -47,7 +47,7 @@ CreateMockWizardScreenHelper<T>::Create(WizardController* wizard) { template <> MockOutShowHide<chromeos::NetworkScreen>* CreateMockWizardScreenHelper<chromeos::NetworkScreen>::Create( WizardController* wizard) { - return new MockOutShowHide<chromeos::NetworkScreen>(wizard, true); + return new MockOutShowHide<chromeos::NetworkScreen>(wizard); } #define MOCK(mock_var, screen_name, mocked_class) \ @@ -71,7 +71,7 @@ class WizardControllerTest : public chromeos::WizardInProcessBrowserTest { IN_PROC_BROWSER_TEST_F(WizardControllerTest, SwitchLanguage) { WizardController* const wizard = controller(); ASSERT_TRUE(wizard != NULL); - wizard->ShowFirstScreen(WizardController::kOutOfBoxScreenName); + wizard->ShowFirstScreen(WizardController::kNetworkScreenName); views::View* current_screen = wizard->contents(); ASSERT_TRUE(current_screen != NULL); @@ -105,6 +105,9 @@ class WizardControllerFlowTest : public WizardControllerTest { virtual Browser* CreateBrowser(Profile* profile) { Browser* ret = WizardControllerTest::CreateBrowser(profile); + // Make sure that OOBE is run as an "official" build. + WizardController::default_controller()->is_official_build_ = true; + // Set up the mocks for all screens. MOCK(mock_account_screen_, account_screen_, chromeos::AccountScreen); MOCK(mock_login_screen_, login_screen_, chromeos::LoginScreen); @@ -115,7 +118,7 @@ class WizardControllerFlowTest : public WizardControllerTest { // Switch to the initial screen. EXPECT_EQ(NULL, controller()->current_screen()); EXPECT_CALL(*mock_network_screen_, Show()).Times(1); - controller()->ShowFirstScreen(WizardController::kOutOfBoxScreenName); + controller()->ShowFirstScreen(WizardController::kNetworkScreenName); return ret; } @@ -133,19 +136,20 @@ class WizardControllerFlowTest : public WizardControllerTest { IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowMain) { EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); + controller()->OnExit(chromeos::ScreenObserver::NETWORK_CONNECTED); + + EXPECT_EQ(controller()->GetEulaScreen(), controller()->current_screen()); + EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1); EXPECT_CALL(*mock_update_screen_, StartUpdate()).Times(1); EXPECT_CALL(*mock_update_screen_, Show()).Times(1); - controller()->OnExit(chromeos::ScreenObserver::NETWORK_CONNECTED); + controller()->OnExit(chromeos::ScreenObserver::EULA_ACCEPTED); EXPECT_EQ(controller()->GetUpdateScreen(), controller()->current_screen()); EXPECT_CALL(*mock_update_screen_, Hide()).Times(1); - EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); - controller()->OnExit(chromeos::ScreenObserver::UPDATE_INSTALLED); - - EXPECT_EQ(controller()->GetEulaScreen(), controller()->current_screen()); - EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_eula_screen_, Show()).Times(0); EXPECT_CALL(*mock_login_screen_, Show()).Times(1); - controller()->OnExit(chromeos::ScreenObserver::EULA_ACCEPTED); + controller()->OnExit(chromeos::ScreenObserver::UPDATE_INSTALLED); EXPECT_EQ(controller()->GetLoginScreen(), controller()->current_screen()); EXPECT_CALL(*mock_login_screen_, Hide()).Times(1); @@ -163,19 +167,43 @@ IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowMain) { IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowErrorUpdate) { EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); - EXPECT_CALL(*mock_update_screen_, StartUpdate()).Times(1); - EXPECT_CALL(*mock_update_screen_, Show()).Times(1); + EXPECT_CALL(*mock_update_screen_, StartUpdate()).Times(0); + EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); + EXPECT_CALL(*mock_update_screen_, Show()).Times(0); EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); controller()->OnExit(chromeos::ScreenObserver::NETWORK_CONNECTED); + EXPECT_EQ(controller()->GetEulaScreen(), controller()->current_screen()); + EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_update_screen_, StartUpdate()).Times(1); + EXPECT_CALL(*mock_update_screen_, Show()).Times(1); + controller()->OnExit(chromeos::ScreenObserver::EULA_ACCEPTED); + EXPECT_EQ(controller()->GetUpdateScreen(), controller()->current_screen()); EXPECT_CALL(*mock_update_screen_, Hide()).Times(1); - EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); + EXPECT_CALL(*mock_eula_screen_, Show()).Times(0); EXPECT_CALL(*mock_eula_screen_, Hide()).Times(0); // last transition + EXPECT_CALL(*mock_login_screen_, Show()).Times(1); controller()->OnExit( chromeos::ScreenObserver::UPDATE_ERROR_UPDATING); + EXPECT_EQ(controller()->GetLoginScreen(), controller()->current_screen()); +} + +IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowEulaDeclined) { + EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); + EXPECT_CALL(*mock_update_screen_, StartUpdate()).Times(0); + EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); + EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); + controller()->OnExit(chromeos::ScreenObserver::NETWORK_CONNECTED); + EXPECT_EQ(controller()->GetEulaScreen(), controller()->current_screen()); + EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_network_screen_, Show()).Times(1); + EXPECT_CALL(*mock_network_screen_, Hide()).Times(0); // last transition + controller()->OnExit(chromeos::ScreenObserver::EULA_BACK); + + EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); } IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowErrorNetwork) { @@ -191,41 +219,46 @@ IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, Accelerators) { views::FocusManager* focus_manager = controller()->contents()->GetFocusManager(); - views::Accelerator accel_account_screen(base::VKEY_A, false, true, true); - views::Accelerator accel_login_screen(base::VKEY_L, false, true, true); - views::Accelerator accel_network_screen(base::VKEY_N, false, true, true); - views::Accelerator accel_update_screen(base::VKEY_U, false, true, true); - views::Accelerator accel_image_screen(base::VKEY_I, false, true, true); - views::Accelerator accel_eula_screen(base::VKEY_E, false, true, true); + views::Accelerator accel_account_screen(app::VKEY_A, false, true, true); + views::Accelerator accel_login_screen(app::VKEY_L, false, true, true); + views::Accelerator accel_network_screen(app::VKEY_N, false, true, true); + views::Accelerator accel_update_screen(app::VKEY_U, false, true, true); + views::Accelerator accel_image_screen(app::VKEY_I, false, true, true); + views::Accelerator accel_eula_screen(app::VKEY_E, false, true, true); EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); EXPECT_CALL(*mock_account_screen_, Show()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_account_screen)); EXPECT_EQ(controller()->GetAccountScreen(), controller()->current_screen()); + focus_manager = controller()->contents()->GetFocusManager(); EXPECT_CALL(*mock_account_screen_, Hide()).Times(1); EXPECT_CALL(*mock_login_screen_, Show()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_login_screen)); EXPECT_EQ(controller()->GetLoginScreen(), controller()->current_screen()); + focus_manager = controller()->contents()->GetFocusManager(); EXPECT_CALL(*mock_login_screen_, Hide()).Times(1); EXPECT_CALL(*mock_network_screen_, Show()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_network_screen)); EXPECT_EQ(controller()->GetNetworkScreen(), controller()->current_screen()); + focus_manager = controller()->contents()->GetFocusManager(); EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); EXPECT_CALL(*mock_update_screen_, Show()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_update_screen)); EXPECT_EQ(controller()->GetUpdateScreen(), controller()->current_screen()); + focus_manager = controller()->contents()->GetFocusManager(); EXPECT_CALL(*mock_update_screen_, Hide()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_image_screen)); EXPECT_EQ(controller()->GetUserImageScreen(), controller()->current_screen()); + focus_manager = controller()->contents()->GetFocusManager(); EXPECT_CALL(*mock_eula_screen_, Show()).Times(1); EXPECT_TRUE(focus_manager->ProcessAccelerator(accel_eula_screen)); EXPECT_EQ(controller()->GetEulaScreen(), controller()->current_screen()); } -COMPILE_ASSERT(chromeos::ScreenObserver::EXIT_CODES_COUNT == 17, +COMPILE_ASSERT(chromeos::ScreenObserver::EXIT_CODES_COUNT == 18, add_tests_for_new_control_flow_you_just_introduced); diff --git a/chrome/browser/chromeos/login/wizard_in_process_browser_test.h b/chrome/browser/chromeos/login/wizard_in_process_browser_test.h index d9b634e..da23b35 100644 --- a/chrome/browser/chromeos/login/wizard_in_process_browser_test.h +++ b/chrome/browser/chromeos/login/wizard_in_process_browser_test.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_IN_PROCESS_BROWSER_TEST_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_IN_PROCESS_BROWSER_TEST_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/login/wizard_screen.h b/chrome/browser/chromeos/login/wizard_screen.h index 3eb3e9e..d0b0d8b 100644 --- a/chrome/browser/chromeos/login/wizard_screen.h +++ b/chrome/browser/chromeos/login/wizard_screen.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_SCREEN_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_SCREEN_H_ +#pragma once #include "gfx/canvas.h" @@ -11,6 +12,9 @@ class WizardScreen; namespace chromeos { class ScreenObserver; } // namespace chromeos +namespace gfx { +class Size; +} // namespace gfx namespace views { class View; } // namespace views @@ -36,6 +40,8 @@ class WizardScreen { virtual void Show() = 0; // Makes wizard screen invisible. virtual void Hide() = 0; + // Returns the size the screen. + virtual gfx::Size GetScreenSize() const = 0; protected: explicit WizardScreen(WizardScreenDelegate* delegate): delegate_(delegate) {} @@ -51,4 +57,3 @@ class WizardScreen { }; #endif // CHROME_BROWSER_CHROMEOS_LOGIN_WIZARD_SCREEN_H_ - diff --git a/chrome/browser/chromeos/low_battery_observer.cc b/chrome/browser/chromeos/low_battery_observer.cc index b923f08..0513cc2 100644 --- a/chrome/browser/chromeos/low_battery_observer.cc +++ b/chrome/browser/chromeos/low_battery_observer.cc @@ -72,7 +72,7 @@ void LowBatteryObserver::PowerChanged(PowerLibrary* object) { void LowBatteryObserver::Show(base::TimeDelta remaining, bool urgent) { notification_.Show(l10n_util::GetStringFUTF16(IDS_LOW_BATTERY_MESSAGE, - WideToUTF16(TimeFormat::TimeRemaining(remaining))), urgent); + TimeFormat::TimeRemaining(remaining)), urgent, true); remaining_ = remaining.InMinutes(); } @@ -81,4 +81,3 @@ void LowBatteryObserver::Hide() { } } // namespace chromeos - diff --git a/chrome/browser/chromeos/low_battery_observer.h b/chrome/browser/chromeos/low_battery_observer.h index 5bbed0a..1756f50 100644 --- a/chrome/browser/chromeos/low_battery_observer.h +++ b/chrome/browser/chromeos/low_battery_observer.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOW_BATTERY_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_LOW_BATTERY_OBSERVER_H_ +#pragma once #include "base/basictypes.h" #include "base/time.h" diff --git a/chrome/browser/chromeos/mock_cros_settings.cc b/chrome/browser/chromeos/mock_cros_settings.cc deleted file mode 100644 index 5cec066..0000000 --- a/chrome/browser/chromeos/mock_cros_settings.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2010 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/mock_cros_settings.h" - -namespace chromeos { - -MockCrosSettings::MockCrosSettings() - : dict_(new DictionaryValue) { - // Some mock settings - SetBoolean(kAccountsPrefAllowBWSI, true); - SetBoolean(kAccountsPrefAllowGuest, true); - - ListValue* user_list = new ListValue; - - DictionaryValue* mock_user = new DictionaryValue; - mock_user->SetString(L"email", L"mock_user_1@gmail.com"); - user_list->Append(mock_user); - - mock_user = new DictionaryValue; - mock_user->SetString(L"email", L"mock_user_2@gmail.com"); - user_list->Append(mock_user); - - Set(kAccountsPrefUsers, user_list); -} - -void MockCrosSettings::Set(const std::wstring& path, Value* in_value) { - dict_->Set(path, in_value); -} - -bool MockCrosSettings::Get(const std::wstring& path, Value** out_value) const { - return dict_->Get(path, out_value); -} - -} // namespace chromeos diff --git a/chrome/browser/chromeos/mock_cros_settings.h b/chrome/browser/chromeos/mock_cros_settings.h deleted file mode 100644 index 606dbdf..0000000 --- a/chrome/browser/chromeos/mock_cros_settings.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2010 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_MOCK_CROS_SETTINGS_H_ -#define CHROME_BROWSER_CHROMEOS_MOCK_CROS_SETTINGS_H_ - -#include "base/singleton.h" -#include "base/scoped_ptr.h" -#include "chrome/browser/chromeos/cros_settings.h" - -namespace chromeos { - -class MockCrosSettings : public CrosSettings { - public: - virtual void Set(const std::wstring& path, Value* in_value); - virtual bool Get(const std::wstring& path, Value** out_value) const; - - private: - scoped_ptr<DictionaryValue> dict_; - - MockCrosSettings(); - friend struct DefaultSingletonTraits<MockCrosSettings>; - - DISALLOW_COPY_AND_ASSIGN(MockCrosSettings); -}; - -} // namespace chromeos - -#endif // CHROME_BROWSER_CHROMEOS_MOCK_CROS_SETTINGS_H_ diff --git a/chrome/browser/chromeos/native_dialog_window.cc b/chrome/browser/chromeos/native_dialog_window.cc index ec9ffbf..200dd62 100644 --- a/chrome/browser/chromeos/native_dialog_window.cc +++ b/chrome/browser/chromeos/native_dialog_window.cc @@ -4,9 +4,12 @@ #include "chrome/browser/chromeos/native_dialog_window.h" +#include <gtk/gtk.h> + #include "app/gtk_signal.h" #include "base/logging.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/frame/bubble_window.h" #include "views/controls/native/native_view_host.h" #include "views/window/dialog_delegate.h" #include "views/window/non_client_view.h" @@ -18,6 +21,30 @@ const int kDialogPadding = 3; const char kNativeDialogHost[] = "_chromeos_native_dialog_host_"; +// TODO(xiyuan): Use gtk_window_get_default_widget with GTK 2.14+. +// Gets the default widget of given dialog. +GtkWidget* GetDialogDefaultWidget(GtkDialog* dialog) { + GtkWidget* default_widget = NULL; + + GList* children = gtk_container_get_children( + GTK_CONTAINER(dialog->action_area)); + + GList* current = children; + while (current) { + GtkWidget* widget = reinterpret_cast<GtkWidget*>(current->data); + if (GTK_WIDGET_HAS_DEFAULT(widget)) { + default_widget = widget; + break; + } + + current = g_list_next(current); + } + + g_list_free(children); + + return default_widget; +} + } // namespace namespace chromeos { @@ -165,6 +192,12 @@ void NativeDialogHost::Init() { if (contents_view_) return; + // Get default widget of the dialog. + GtkWidget* default_widget = GetDialogDefaultWidget(GTK_DIALOG(dialog_)); + + // Get focus widget of the dialog. + GtkWidget* focus_widget = gtk_window_get_focus(GTK_WINDOW(dialog_)); + // Create a GtkAlignment as dialog contents container. GtkWidget* contents = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); gtk_alignment_set_padding(GTK_ALIGNMENT(contents), @@ -185,6 +218,8 @@ void NativeDialogHost::Init() { gtk_widget_show_all(contents); contents_view_ = new views::NativeViewHost(); + contents_view_->set_background(views::Background::CreateSolidBackground( + BubbleWindow::kBackgroundColor)); AddChildView(contents_view_); contents_view_->Attach(contents); @@ -207,6 +242,12 @@ void NativeDialogHost::Init() { } CheckSize(); + + if (default_widget) + gtk_widget_grab_default(default_widget); + + if (focus_widget) + gtk_widget_grab_focus(focus_widget); } void NativeDialogHost::CheckSize() { @@ -224,7 +265,7 @@ void ShowNativeDialog(gfx::NativeWindow parent, const gfx::Size& min_size) { NativeDialogHost* native_dialog_host = new NativeDialogHost(native_dialog, flags, size, min_size); - views::Window::CreateChromeWindow(parent, gfx::Rect(), native_dialog_host); + BubbleWindow::Create(parent, gfx::Rect(), native_dialog_host); native_dialog_host->window()->Show(); } diff --git a/chrome/browser/chromeos/native_dialog_window.h b/chrome/browser/chromeos/native_dialog_window.h index 6792231..47a478b 100644 --- a/chrome/browser/chromeos/native_dialog_window.h +++ b/chrome/browser/chromeos/native_dialog_window.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_NATIVE_DIALOG_WINDOW_H_ #define CHROME_BROWSER_CHROMEOS_NATIVE_DIALOG_WINDOW_H_ +#pragma once #include "gfx/native_widget_types.h" diff --git a/chrome/browser/chromeos/network_list.cc b/chrome/browser/chromeos/network_list.cc index f92190f..9780642 100644 --- a/chrome/browser/chromeos/network_list.cc +++ b/chrome/browser/chromeos/network_list.cc @@ -5,7 +5,7 @@ #include "chrome/browser/chromeos/network_list.h" #include "app/l10n_util.h" -#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "grit/generated_resources.h" diff --git a/chrome/browser/chromeos/network_list.h b/chrome/browser/chromeos/network_list.h index f08b23a..5b320fc 100644 --- a/chrome/browser/chromeos/network_list.h +++ b/chrome/browser/chromeos/network_list.h @@ -4,10 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_NETWORK_LIST_H_ #define CHROME_BROWSER_CHROMEOS_NETWORK_LIST_H_ +#pragma once #include <string> #include <vector> +#include "base/string16.h" #include "chrome/browser/chromeos/cros/network_library.h" namespace chromeos { diff --git a/chrome/browser/chromeos/network_message_observer.cc b/chrome/browser/chromeos/network_message_observer.cc index 192ff28..c87a21b 100644 --- a/chrome/browser/chromeos/network_message_observer.cc +++ b/chrome/browser/chromeos/network_message_observer.cc @@ -5,28 +5,125 @@ #include "chrome/browser/chromeos/network_message_observer.h" #include "app/l10n_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/network_library.h" +#include "chrome/browser/chromeos/options/network_config_view.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" +#include "views/window/dialog_delegate.h" +#include "views/window/window.h" namespace chromeos { NetworkMessageObserver::NetworkMessageObserver(Profile* profile) - : notification_(profile, "network_connection.chromeos", + : initialized_(false), + notification_(profile, "network_connection.chromeos", IDR_NOTIFICATION_NETWORK_FAILED, l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE)) { + NetworkChanged(CrosLibrary::Get()->GetNetworkLibrary()); + initialized_ = true; } NetworkMessageObserver::~NetworkMessageObserver() { notification_.Hide(); } +void NetworkMessageObserver::CreateModalPopup(views::WindowDelegate* view) { + Browser* browser = BrowserList::GetLastActive(); + if (browser && browser->type() != Browser::TYPE_NORMAL) { + browser = BrowserList::FindBrowserWithType(browser->profile(), + Browser::TYPE_NORMAL, + true); + } + DCHECK(browser); + views::Window* window = views::Window::CreateChromeWindow( + browser->window()->GetNativeHandle(), gfx::Rect(), view); + window->SetIsAlwaysOnTop(true); + window->Show(); +} + void NetworkMessageObserver::NetworkChanged(NetworkLibrary* obj) { - // TODO(chocobo): Display open networks notification here. -// notification_.Show(l10n_util::GetStringFUTF16( -// IDS_NETWORK_CONNECTION_ERROR_MESSAGE, -// ASCIIToUTF16(network.name())), true); + const WifiNetworkVector& wifi_networks = obj->wifi_networks(); + const CellularNetworkVector& cellular_networks = obj->cellular_networks(); + + NetworkConfigView* view = NULL; + std::string new_failed_network; + // Check to see if we have any newly failed wifi network. + for (WifiNetworkVector::const_iterator it = wifi_networks.begin(); + it < wifi_networks.end(); it++) { + const WifiNetwork& wifi = *it; + if (wifi.failed()) { + ServicePathWifiMap::iterator iter = + wifi_networks_.find(wifi.service_path()); + // If the network did not previously exist, then don't do anything. + // For example, if the user travels to a location and finds a service + // that has previously failed, we don't want to show a notification. + if (iter == wifi_networks_.end()) + continue; + + const WifiNetwork& wifi_old = iter->second; + // If this network was in a failed state previously, then it's not new. + if (wifi_old.failed()) + continue; + + // Display login box again for bad_passphrase and bad_wepkey errors. + if (wifi.error() == ERROR_BAD_PASSPHRASE || + wifi.error() == ERROR_BAD_WEPKEY) { + // The NetworkConfigView will show the appropriate error message. + view = new NetworkConfigView(wifi, true); + // There should only be one wifi network that failed to connect. + // If for some reason, we have more than one failure, + // we only display the first one. So we break here. + break; + } + + // If network connection failed, display a notification. + // We only do this if we were trying to make a new connection. + // So if a previously connected network got disconnected for any reason, + // we don't display notification. + if (wifi_old.connecting()) { + new_failed_network = wifi.name(); + // Like above, there should only be one newly failed network. + break; + } + } + + // If we find any network connecting, we hide any notifications. + if (wifi.connecting()) { + notification_.Hide(); + } + } + + // Refresh stored networks. + wifi_networks_.clear(); + for (WifiNetworkVector::const_iterator it = wifi_networks.begin(); + it < wifi_networks.end(); it++) { + const WifiNetwork& wifi = *it; + wifi_networks_[wifi.service_path()] = wifi; + } + cellular_networks_.clear(); + for (CellularNetworkVector::const_iterator it = cellular_networks.begin(); + it < cellular_networks.end(); it++) { + const CellularNetwork& cellular = *it; + cellular_networks_[cellular.service_path()] = cellular; + } + + // Show notification if necessary. + if (!new_failed_network.empty()) { + // Hide if already shown to force show it in case user has closed it. + if (notification_.visible()) + notification_.Hide(); + notification_.Show(l10n_util::GetStringFUTF16( + IDS_NETWORK_CONNECTION_ERROR_MESSAGE, + ASCIIToUTF16(new_failed_network)), false, true); + } + + // Show login box if necessary. + if (view && initialized_) + CreateModalPopup(view); } } // namespace chromeos diff --git a/chrome/browser/chromeos/network_message_observer.h b/chrome/browser/chromeos/network_message_observer.h index a62e636..554628f 100644 --- a/chrome/browser/chromeos/network_message_observer.h +++ b/chrome/browser/chromeos/network_message_observer.h @@ -4,8 +4,9 @@ #ifndef CHROME_BROWSER_CHROMEOS_NETWORK_MESSAGE_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_NETWORK_MESSAGE_OBSERVER_H_ +#pragma once -#include <set> +#include <map> #include <string> #include "base/basictypes.h" @@ -13,6 +14,9 @@ #include "chrome/browser/chromeos/notifications/system_notification.h" class Profile; +namespace views { +class WindowDelegate; +} namespace chromeos { @@ -24,10 +28,19 @@ class NetworkMessageObserver : public NetworkLibrary::Observer { explicit NetworkMessageObserver(Profile* profile); virtual ~NetworkMessageObserver(); + typedef std::map<std::string, WifiNetwork> ServicePathWifiMap; + typedef std::map<std::string, CellularNetwork> ServicePathCellularMap; private: + virtual void CreateModalPopup(views::WindowDelegate* view); + + // NetworkLibrary::Observer implementation. virtual void NetworkChanged(NetworkLibrary* obj); - virtual void NetworkTraffic(NetworkLibrary* obj, int traffic_type) {} + bool initialized_; + // Wifi networks by service path. + ServicePathWifiMap wifi_networks_; + // Cellular networks by service path. + ServicePathCellularMap cellular_networks_; SystemNotification notification_; DISALLOW_COPY_AND_ASSIGN(NetworkMessageObserver); diff --git a/chrome/browser/chromeos/network_state_notifier.h b/chrome/browser/chromeos/network_state_notifier.h index a64f264..9d49d94 100644 --- a/chrome/browser/chromeos/network_state_notifier.h +++ b/chrome/browser/chromeos/network_state_notifier.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_NETWORK_STATE_NOTIFIER_H_ #define CHROME_BROWSER_CHROMEOS_NETWORK_STATE_NOTIFIER_H_ +#pragma once #include "chrome/browser/chromeos/cros/network_library.h" @@ -60,7 +61,6 @@ class NetworkStateNotifier : public NetworkLibrary::Observer { // NetworkLibrary::Observer implementation. virtual void NetworkChanged(NetworkLibrary* cros); - virtual void NetworkTraffic(NetworkLibrary* cros, int traffic_type) {} private: friend struct DefaultSingletonTraits<NetworkStateNotifier>; diff --git a/chrome/browser/chromeos/network_state_notifier_browsertest.cc b/chrome/browser/chromeos/network_state_notifier_browsertest.cc index 520dbcf..4833d48 100644 --- a/chrome/browser/chromeos/network_state_notifier_browsertest.cc +++ b/chrome/browser/chromeos/network_state_notifier_browsertest.cc @@ -7,11 +7,11 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" #include "chrome/browser/chromeos/cros/mock_network_library.h" -#include "chrome/common/notification_service.h" #include "chrome/common/notification_registrar.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gmock/include/gmock/gmock.h" +#include "chrome/common/notification_service.h" #include "chrome/test/ui_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -21,15 +21,16 @@ using ::testing::_; class NetworkStateNotifierTest : public CrosInProcessBrowserTest, public NotificationObserver { public: - NetworkStateNotifierTest() { + NetworkStateNotifierTest() : mock_network_library_(NULL) { } protected: virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - SetStatusAreaMocksExpectations(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->SetStatusAreaMocksExpectations(); // Initialize network state notifier. ASSERT_TRUE(CrosLibrary::Get()->EnsureLoaded()); + mock_network_library_ = cros_mock_->mock_network_library(); ASSERT_TRUE(mock_network_library_); EXPECT_CALL(*mock_network_library_, Connected()) .Times(1) @@ -55,6 +56,7 @@ class NetworkStateNotifierTest : public CrosInProcessBrowserTest, } protected: + MockNetworkLibrary *mock_network_library_; NetworkStateDetails::State state_; }; diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc index 31a6ef7..1cc3740 100644 --- a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc +++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc @@ -61,16 +61,28 @@ void BalloonCollectionImpl::Add(const Notification& notification, space_change_listener_->OnBalloonSpaceChanged(); } +bool BalloonCollectionImpl::AddDOMUIMessageCallback( + const Notification& notification, + const std::string& message, + MessageCallback* callback) { + Balloons::iterator iter = FindBalloon(notification); + if (iter == balloons_.end()) { + delete callback; + return false; + } + BalloonViewHost* host = + static_cast<BalloonViewHost*>((*iter)->view()->GetHost()); + return host->AddDOMUIMessageCallback(message, callback); +} + void BalloonCollectionImpl::AddSystemNotification( const Notification& notification, Profile* profile, bool sticky, bool control) { - // TODO(oshima): We need to modify BallonCollection/MakeBalloon pattern - // in order to add unit tests for system notification. Balloon* new_balloon = new Balloon(notification, profile, this); new_balloon->set_view( - new chromeos::BalloonViewImpl(sticky, control)); + new chromeos::BalloonViewImpl(sticky, control, true)); balloons_.push_back(new_balloon); new_balloon->Show(); notification_ui_->Add(new_balloon); @@ -164,7 +176,7 @@ void BalloonCollectionImpl::Shutdown() { Balloon* BalloonCollectionImpl::MakeBalloon(const Notification& notification, Profile* profile) { Balloon* new_balloon = new Balloon(notification, profile, this); - new_balloon->set_view(new chromeos::BalloonViewImpl(false, true)); + new_balloon->set_view(new chromeos::BalloonViewImpl(false, true, false)); return new_balloon; } diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.h b/chrome/browser/chromeos/notifications/balloon_collection_impl.h index 7ae7763..79cfe14 100644 --- a/chrome/browser/chromeos/notifications/balloon_collection_impl.h +++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.h @@ -4,11 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_COLLECTION_IMPL_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_COLLECTION_IMPL_H_ +#pragma once -#include <deque> +#include <string> #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "chrome/browser/chromeos/notifications/balloon_view_host.h" #include "chrome/browser/notifications/balloon_collection.h" #include "chrome/common/notification_registrar.h" #include "gfx/point.h" @@ -70,6 +72,15 @@ class BalloonCollectionImpl : public BalloonCollection, const NotificationSource& source, const NotificationDetails& details); + // Adds a callback for DOMUI message. Returns true if the callback + // is succssfully registered, or false otherwise. It fails to add if + // there is no notification that matches NotificationDelegate::id(), + // or a callback for given message already exists. The callback + // object is owned and deleted by callee. + bool AddDOMUIMessageCallback(const Notification& notification, + const std::string& message, + MessageCallback* callback); + // Adds new system notification. // |sticky| is used to indicate that the notification // is sticky and cannot be dismissed by a user. |controls| turns on/off diff --git a/chrome/browser/chromeos/notifications/balloon_view.cc b/chrome/browser/chromeos/notifications/balloon_view.cc index 6ee8043..24a6004 100644 --- a/chrome/browser/chromeos/notifications/balloon_view.cc +++ b/chrome/browser/chromeos/notifications/balloon_view.cc @@ -11,6 +11,7 @@ #include "app/resource_bundle.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/notifications/balloon_view_host.h" #include "chrome/browser/chromeos/notifications/notification_panel.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/desktop_notification_service.h" @@ -153,12 +154,12 @@ class NotificationControlView : public views::View, void CreateOptionsMenu() { if (options_menu_contents_.get()) return; - const string16 source_label_text = WideToUTF16Hack(l10n_util::GetStringF( + const string16 source_label_text = l10n_util::GetStringFUTF16( IDS_NOTIFICATION_BALLOON_SOURCE_LABEL, - balloon_view_->balloon_->notification().display_source())); - const string16 label_text = WideToUTF16Hack(l10n_util::GetStringF( + balloon_view_->balloon_->notification().display_source()); + const string16 label_text = l10n_util::GetStringFUTF16( IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE, - balloon_view_->balloon_->notification().display_source())); + balloon_view_->balloon_->notification().display_source()); options_menu_contents_.reset(new menus::SimpleMenuModel(this)); // TODO(oshima): Showing the source info in the menu for now. @@ -181,14 +182,15 @@ class NotificationControlView : public views::View, DISALLOW_COPY_AND_ASSIGN(NotificationControlView); }; -BalloonViewImpl::BalloonViewImpl(bool sticky, bool controls) +BalloonViewImpl::BalloonViewImpl(bool sticky, bool controls, bool dom_ui) : balloon_(NULL), html_contents_(NULL), method_factory_(this), stale_(false), sticky_(sticky), controls_(controls), - closed_(false) { + closed_(false), + dom_ui_(dom_ui) { // This object is not to be deleted by the views hierarchy, // as it is owned by the balloon. set_parent_owned(false); @@ -208,8 +210,9 @@ BalloonViewImpl::~BalloonViewImpl() { void BalloonViewImpl::Show(Balloon* balloon) { balloon_ = balloon; - html_contents_ = new BalloonViewHost(balloon); + if (dom_ui_) + html_contents_->EnableDOMUI(); AddChildView(html_contents_->view()); notification_registrar_.Add(this, NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon)); diff --git a/chrome/browser/chromeos/notifications/balloon_view.h b/chrome/browser/chromeos/notifications/balloon_view.h index c6032e6..e275915 100644 --- a/chrome/browser/chromeos/notifications/balloon_view.h +++ b/chrome/browser/chromeos/notifications/balloon_view.h @@ -6,11 +6,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_VIEW_H_ +#pragma once #include "base/basictypes.h" #include "base/scoped_ptr.h" #include "base/task.h" #include "chrome/browser/notifications/balloon.h" +#include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" #include "gfx/path.h" @@ -27,13 +29,13 @@ class TextButton; class WidgetGtk; } // namespace views -class BalloonViewHost; class Notification; class NotificationDetails; class NotificationSource; namespace chromeos { +class BalloonViewHost; class NotificationControlView; // A balloon view is the UI component for a notification panel. @@ -41,8 +43,8 @@ class BalloonViewImpl : public BalloonView, public views::View, public NotificationObserver { public: - BalloonViewImpl(bool sticky, bool controls); - ~BalloonViewImpl(); + BalloonViewImpl(bool sticky, bool controls, bool dom_ui); + virtual ~BalloonViewImpl(); // views::View interface. virtual void Layout(); @@ -122,6 +124,8 @@ class BalloonViewImpl : public BalloonView, bool controls_; // True if the notification is being closed. bool closed_; + // True to enable domui in the notification. + bool dom_ui_; DISALLOW_COPY_AND_ASSIGN(BalloonViewImpl); }; diff --git a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc index cbdf0e4..6824f97 100644 --- a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc +++ b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc @@ -4,6 +4,10 @@ #include "chrome/browser/chromeos/notifications/desktop_notifications_unittest.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "chrome/common/render_messages_params.h" + namespace chromeos { // static @@ -193,7 +197,7 @@ TEST_F(DesktopNotificationsTest, TestManyNotifications) { // Request lots of identical notifications. const int kLotsOfToasts = 20; for (int id = 1; id <= kLotsOfToasts; ++id) { - SCOPED_TRACE(StringPrintf("Creation loop: id=%d", id)); + SCOPED_TRACE(base::StringPrintf("Creation loop: id=%d", id)); ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); params.notification_id = id; EXPECT_TRUE(service_->ShowDesktopNotification( @@ -217,7 +221,7 @@ TEST_F(DesktopNotificationsTest, TestManyNotifications) { for (id = 1; id <= cancelled; ++id) { - SCOPED_TRACE(StringPrintf("Cancel half of notifications: id=%d", id)); + SCOPED_TRACE(base::StringPrintf("Cancel half of notifications: id=%d", id)); service_->CancelDesktopNotification(process_id, route_id, id); MessageLoopForUI::current()->RunAllPending(); expected_log.append("notification closed by script\n"); @@ -228,7 +232,7 @@ TEST_F(DesktopNotificationsTest, TestManyNotifications) { // Now cancel the rest. It should empty the balloon space. for (; id <= kLotsOfToasts; ++id) { - SCOPED_TRACE(StringPrintf("Cancel loop: id=%d", id)); + SCOPED_TRACE(base::StringPrintf("Cancel loop: id=%d", id)); service_->CancelDesktopNotification(process_id, route_id, id); expected_log.append("notification closed by script\n"); MessageLoopForUI::current()->RunAllPending(); @@ -243,7 +247,7 @@ TEST_F(DesktopNotificationsTest, TestEarlyDestruction) { // Create some toasts and then prematurely delete the notification service, // just to make sure nothing crashes/leaks. for (int id = 0; id <= 3; ++id) { - SCOPED_TRACE(StringPrintf("Show Text loop: id=%d", id)); + SCOPED_TRACE(base::StringPrintf("Show Text loop: id=%d", id)); EXPECT_TRUE(service_->ShowDesktopNotification( StandardTestNotification(), 0, 0, diff --git a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.h b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.h index b424c67..fedd4f0 100644 --- a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.h +++ b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_DESKTOP_NOTIFICATIONS_UNITTEST_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_DESKTOP_NOTIFICATIONS_UNITTEST_H_ +#pragma once #include <set> #include <string> diff --git a/chrome/browser/chromeos/notifications/notification_browsertest.cc b/chrome/browser/chromeos/notifications/notification_browsertest.cc index a1e1152..2d76a3d 100644 --- a/chrome/browser/chromeos/notifications/notification_browsertest.cc +++ b/chrome/browser/chromeos/notifications/notification_browsertest.cc @@ -5,6 +5,11 @@ #include "app/x11_util.h" #include "base/message_loop.h" #include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/string16.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/notifications/balloon_collection_impl.h" @@ -26,6 +31,7 @@ class MockNotificationDelegate : public NotificationDelegate { virtual void Display() {} virtual void Error() {} virtual void Close(bool by_user) {} + virtual void Click() {} virtual std::string id() const { return id_; } private: @@ -50,6 +56,10 @@ class NotificationTest : public InProcessBrowserTest, expected_(PanelController::INITIAL) { } + void HandleDOMUIMessage(const ListValue* value) { + MessageLoop::current()->Quit(); + } + protected: virtual void SetUp() { // Detect if we're running under ChromeOS WindowManager. See @@ -249,7 +259,7 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestSystemNotification) { // Dismiss the notification. // TODO(oshima): Consider updating API to Remove(NotificationDelegate) // or Remove(std::string id); - collection->Remove(Notification(GURL(), GURL(), std::wstring(), string16(), + collection->Remove(Notification(GURL(), GURL(), string16(), string16(), delegate.get())); ui_test_utils::RunAllPendingInMessageLoop(); @@ -414,7 +424,8 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestCloseOpen) { EXPECT_EQ(NotificationPanel::CLOSED, tester->state()); } -IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { +// TODO(oshima): bug chromium-os:7139 Fix this flaky test on ChromeOS. +IN_PROC_BROWSER_TEST_F(NotificationTest, FLAKY_TestScrollBalloonToVisible) { BalloonCollectionImpl* collection = GetBalloonCollectionImpl(); NotificationPanel* panel = GetNotificationPanel(); NotificationPanelTester* tester = panel->GetTester(); @@ -426,8 +437,8 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { // new notification is always visible for (int i = 0; i < create_count; i++) { { - SCOPED_TRACE(StringPrintf("new n%d", i)); - std::string id = StringPrintf("n%d", i); + SCOPED_TRACE(base::StringPrintf("new n%d", i)); + std::string id = base::StringPrintf("n%d", i); collection->Add(NewMockNotification(id), profile); EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state()); BalloonViewImpl* view = @@ -436,8 +447,8 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { EXPECT_TRUE(tester->IsVisible(view)); } { - SCOPED_TRACE(StringPrintf("new s%d", i)); - std::string id = StringPrintf("s%d", i); + SCOPED_TRACE(base::StringPrintf("new s%d", i)); + std::string id = base::StringPrintf("s%d", i); collection->AddSystemNotification( NewMockNotification(id), browser()->profile(), true, false); ui_test_utils::RunAllPendingInMessageLoop(); @@ -450,8 +461,8 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { // Update should not change the visibility for (int i = 0; i < create_count; i++) { { - SCOPED_TRACE(StringPrintf("update n%d", i)); - Notification notify = NewMockNotification(StringPrintf("n%d", i)); + SCOPED_TRACE(base::StringPrintf("update n%d", i)); + Notification notify = NewMockNotification(base::StringPrintf("n%d", i)); // The last shown notification is sticky, which makes all non sticky // invisible. EXPECT_TRUE(collection->UpdateNotification(notify)); @@ -460,8 +471,8 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { EXPECT_FALSE(tester->IsVisible(view)); } { - SCOPED_TRACE(StringPrintf("update s%d", i)); - Notification notify = NewMockNotification(StringPrintf("s%d", i)); + SCOPED_TRACE(base::StringPrintf("update s%d", i)); + Notification notify = NewMockNotification(base::StringPrintf("s%d", i)); BalloonViewImpl* view = tester->GetBalloonView(collection, notify); bool currently_visible = tester->IsVisible(view); EXPECT_TRUE(collection->UpdateNotification(notify)); @@ -473,16 +484,16 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestScrollBalloonToVisible) { // UpdateAndShowNotification makes notification visible for (int i = 0; i < create_count; i++) { { - SCOPED_TRACE(StringPrintf("update and show n%d", i)); - Notification notify = NewMockNotification(StringPrintf("n%d", i)); + SCOPED_TRACE(base::StringPrintf("update and show n%d", i)); + Notification notify = NewMockNotification(base::StringPrintf("n%d", i)); EXPECT_TRUE(collection->UpdateAndShowNotification(notify)); ui_test_utils::RunAllPendingInMessageLoop(); BalloonViewImpl* view = tester->GetBalloonView(collection, notify); EXPECT_TRUE(tester->IsVisible(view)); } { - SCOPED_TRACE(StringPrintf("update and show s%d", i)); - Notification notify = NewMockNotification(StringPrintf("s%d", i)); + SCOPED_TRACE(base::StringPrintf("update and show s%d", i)); + Notification notify = NewMockNotification(base::StringPrintf("s%d", i)); EXPECT_TRUE(collection->UpdateAndShowNotification(notify)); ui_test_utils::RunAllPendingInMessageLoop(); BalloonViewImpl* view = tester->GetBalloonView(collection, notify); @@ -546,4 +557,58 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestCloseDismissAllNonSticky) { EXPECT_EQ(1, tester->GetStickyNotificationCount()); } +IN_PROC_BROWSER_TEST_F(NotificationTest, TestAddDOMUIMessageCallback) { + BalloonCollectionImpl* collection = GetBalloonCollectionImpl(); + Profile* profile = browser()->profile(); + + collection->AddSystemNotification( + NewMockNotification("1"), profile, false, false); + + EXPECT_TRUE(collection->AddDOMUIMessageCallback( + NewMockNotification("1"), + "test", + NewCallback( + static_cast<NotificationTest*>(this), + &NotificationTest::HandleDOMUIMessage))); + + // Adding callback for the same message twice should fail. + EXPECT_FALSE(collection->AddDOMUIMessageCallback( + NewMockNotification("1"), + "test", + NewCallback( + static_cast<NotificationTest*>(this), + &NotificationTest::HandleDOMUIMessage))); + + // Adding callback to nonexistent notification should fail. + EXPECT_FALSE(collection->AddDOMUIMessageCallback( + NewMockNotification("2"), + "test1", + NewCallback( + static_cast<NotificationTest*>(this), + &NotificationTest::HandleDOMUIMessage))); +} + +IN_PROC_BROWSER_TEST_F(NotificationTest, TestDOMUIMessageCallback) { + BalloonCollectionImpl* collection = GetBalloonCollectionImpl(); + Profile* profile = browser()->profile(); + // a notification that sends 'test' domui message back to chrome. + const GURL content_url( + "data:text/html;charset=utf-8," + "<html><script>function send() { chrome.send('test', ['']); }</script>" + "<body onload='send()'></body></html>"); + collection->AddSystemNotification( + Notification(GURL(), content_url, string16(), string16(), + new MockNotificationDelegate("1")), + profile, + false, + false); + EXPECT_TRUE(collection->AddDOMUIMessageCallback( + NewMockNotification("1"), + "test", + NewCallback( + static_cast<NotificationTest*>(this), + &NotificationTest::HandleDOMUIMessage))); + MessageLoop::current()->Run(); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/notifications/notification_panel.cc b/chrome/browser/chromeos/notifications/notification_panel.cc index 0f53f9f..2b48256 100644 --- a/chrome/browser/chromeos/notifications/notification_panel.cc +++ b/chrome/browser/chromeos/notifications/notification_panel.cc @@ -10,9 +10,9 @@ #include "app/resource_bundle.h" #include "chrome/browser/chromeos/notifications/balloon_collection_impl.h" #include "chrome/browser/chromeos/notifications/balloon_view.h" +#include "cros/chromeos_wm_ipc_enums.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" -#include "third_party/cros/chromeos_wm_ipc_enums.h" #include "views/background.h" #include "views/controls/native/native_view_host.h" #include "views/controls/scroll_view.h" diff --git a/chrome/browser/chromeos/notifications/notification_panel.h b/chrome/browser/chromeos/notifications/notification_panel.h index 7b795e4..96739bc 100644 --- a/chrome/browser/chromeos/notifications/notification_panel.h +++ b/chrome/browser/chromeos/notifications/notification_panel.h @@ -6,9 +6,10 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_NOTIFICATION_PANEL_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_NOTIFICATION_PANEL_H_ +#pragma once -#include "base/task.h" #include "base/scoped_ptr.h" +#include "base/task.h" #include "chrome/browser/chromeos/frame/panel_controller.h" #include "chrome/browser/chromeos/notifications/balloon_collection_impl.h" #include "chrome/common/notification_registrar.h" diff --git a/chrome/browser/chromeos/notifications/system_notification.cc b/chrome/browser/chromeos/notifications/system_notification.cc index f2d9d4a..ad54253 100644 --- a/chrome/browser/chromeos/notifications/system_notification.cc +++ b/chrome/browser/chromeos/notifications/system_notification.cc @@ -4,11 +4,10 @@ #include "chrome/browser/chromeos/notifications/system_notification.h" -#include "app/resource_bundle.h" -#include "base/base64.h" #include "base/move.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/notifications/system_notification_factory.h" +#include "chrome/browser/dom_ui/dom_ui_util.h" #include "chrome/browser/notifications/notification.h" #include "chrome/browser/notifications/notification_ui_manager.h" @@ -23,15 +22,9 @@ SystemNotification::SystemNotification(Profile* profile, std::string id, title_(move(title)), visible_(false), urgent_(false) { - // Load resource icon and covert to base64 encoded data url - scoped_refptr<RefCountedMemory> raw_icon(ResourceBundle::GetSharedInstance(). - LoadDataResourceBytes(icon_resource_id)); - std::string str_gurl; - std::copy(raw_icon->front(), raw_icon->front() + raw_icon->size(), - std::back_inserter(str_gurl)); - base::Base64Encode(str_gurl, &str_gurl); - str_gurl.insert(0, "data:image/png;base64,"); - GURL tmp_gurl(str_gurl); + std::string url = dom_ui_util::GetImageDataUrlFromResource(icon_resource_id); + DCHECK(!url.empty()); + GURL tmp_gurl(url); icon_.Swap(&tmp_gurl); } @@ -39,7 +32,9 @@ SystemNotification::~SystemNotification() { Hide(); } -void SystemNotification::Show(const string16& message, bool urgent) { +void SystemNotification::Show(const string16& message, + bool urgent, + bool sticky) { Notification notify = SystemNotificationFactory::Create(icon_, title_, message, delegate_.get()); if (visible_) { @@ -50,7 +45,8 @@ void SystemNotification::Show(const string16& message, bool urgent) { collection_->UpdateNotification(notify); } } else { - collection_->AddSystemNotification(notify, profile_, true /* sticky */, + collection_->AddSystemNotification(notify, profile_, + sticky, false /* no controls */); } visible_ = true; @@ -59,7 +55,7 @@ void SystemNotification::Show(const string16& message, bool urgent) { void SystemNotification::Hide() { if (visible_) { - collection_->Remove(Notification(GURL(), GURL(), std::wstring(), string16(), + collection_->Remove(Notification(GURL(), GURL(), string16(), string16(), delegate_.get())); visible_ = false; diff --git a/chrome/browser/chromeos/notifications/system_notification.h b/chrome/browser/chromeos/notifications/system_notification.h index ff1efef..a5ac224 100644 --- a/chrome/browser/chromeos/notifications/system_notification.h +++ b/chrome/browser/chromeos/notifications/system_notification.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_SYSTEM_NOTIFICATION_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_SYSTEM_NOTIFICATION_H_ +#pragma once #include <string> @@ -29,12 +30,12 @@ class SystemNotification { SystemNotification(Profile* profile, std::string id, int icon_resource_id, string16 title); - ~SystemNotification(); + virtual ~SystemNotification(); // Show will show or update the message for this notification // on a transition to urgent, the notification will be shown if it was // previously hidden or minimized by the user. - void Show(const string16& message, bool urgent); + void Show(const string16& message, bool urgent, bool sticky); // Hide will dismiss the notification, if the notification is already // hidden it does nothing @@ -53,6 +54,7 @@ class SystemNotification { void Display() {} void Error() {} void Close(bool by_user) {} + void Click() {} std::string id() const { return id_; } private: @@ -75,4 +77,3 @@ class SystemNotification { } // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_SYSTEM_NOTIFICATION_H_ - diff --git a/chrome/browser/chromeos/notifications/system_notification_factory.cc b/chrome/browser/chromeos/notifications/system_notification_factory.cc index ac01322..51b308f 100644 --- a/chrome/browser/chromeos/notifications/system_notification_factory.cc +++ b/chrome/browser/chromeos/notifications/system_notification_factory.cc @@ -15,7 +15,8 @@ Notification SystemNotificationFactory::Create( NotificationDelegate* delegate) { string16 content_url = DesktopNotificationService::CreateDataUrl( icon, title, text, WebKit::WebTextDirectionDefault); - return Notification(GURL(), GURL(content_url), std::wstring(), string16(), + return Notification(GURL(), GURL(content_url), string16(), string16(), delegate); } + } // namespace chromeos diff --git a/chrome/browser/chromeos/notifications/system_notification_factory.h b/chrome/browser/chromeos/notifications/system_notification_factory.h index 6acd53a..c81b07d 100644 --- a/chrome/browser/chromeos/notifications/system_notification_factory.h +++ b/chrome/browser/chromeos/notifications/system_notification_factory.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_SYSTEM_NOTIFICATION_FACTORY_H_ #define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_SYSTEM_NOTIFICATION_FACTORY_H_ +#pragma once #include "base/basictypes.h" #include "chrome/browser/notifications/notification.h" diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc index 0dc9826..ece8fb6 100644 --- a/chrome/browser/chromeos/offline/offline_load_page.cc +++ b/chrome/browser/chromeos/offline/offline_load_page.cc @@ -9,6 +9,8 @@ #include "base/histogram.h" #include "base/i18n/rtl.h" #include "base/string_piece.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser.h" #include "chrome/browser/chrome_thread.h" @@ -27,8 +29,8 @@ namespace { const int kMaxBlankPeriod = 3000; // A utility function to set the dictionary's value given by |resource_id|. -void SetString(DictionaryValue* strings, const wchar_t* name, int resource_id) { - strings->SetString(name, l10n_util::GetString(resource_id)); +void SetString(DictionaryValue* strings, const char* name, int resource_id) { + strings->SetString(name, l10n_util::GetStringUTF16(resource_id)); } } // namespace @@ -61,27 +63,28 @@ OfflineLoadPage::OfflineLoadPage(TabContents* tab_contents, std::string OfflineLoadPage::GetHTMLContents() { DictionaryValue strings; - SetString(&strings, L"headLine", IDS_OFFLINE_LOAD_HEADLINE); - SetString(&strings, L"description", IDS_OFFLINE_LOAD_DESCRIPTION); - SetString(&strings, L"load_button", IDS_OFFLINE_LOAD_BUTTON); - SetString(&strings, L"back_button", IDS_OFFLINE_BACK_BUTTON); + SetString(&strings, "headLine", IDS_OFFLINE_LOAD_HEADLINE); + SetString(&strings, "description", IDS_OFFLINE_LOAD_DESCRIPTION); + SetString(&strings, "load_button", IDS_OFFLINE_LOAD_BUTTON); + SetString(&strings, "back_button", IDS_OFFLINE_BACK_BUTTON); // TODO(oshima): tab()->GetTitle() always return url. This has to be // a cached title. - strings.SetString(L"title", UTF16ToWide(tab()->GetTitle())); - strings.SetString(L"textdirection", base::i18n::IsRTL() ? L"rtl" : L"ltr"); - strings.SetString(L"display_go_back", - tab()->controller().CanGoBack() ? L"inline" : L"none"); + strings.SetString("title", tab()->GetTitle()); + strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); + strings.SetString("display_go_back", + tab()->controller().CanGoBack() ? "inline" : "none"); int64 time_to_wait = std::max( static_cast<int64>(0), kMaxBlankPeriod - NetworkStateNotifier::GetOfflineDuration().InMilliseconds()); - strings.SetString(L"on_load", - StringPrintf(L"startTimer(%ld)", time_to_wait)); + strings.SetString("on_load", + WideToUTF16Hack(base::StringPrintf(L"startTimer(%ld)", + time_to_wait))); // TODO(oshima): thumbnail is not working yet. fix this. const std::string url = "chrome://thumb/" + GetURL().spec(); - strings.SetString(L"thumbnailUrl", "url(" + url + ")"); + strings.SetString("thumbnailUrl", "url(" + url + ")"); base::StringPiece html( ResourceBundle::GetSharedInstance().GetRawDataResource( diff --git a/chrome/browser/chromeos/offline/offline_load_page.h b/chrome/browser/chromeos/offline/offline_load_page.h index 3363853..b6eb046 100644 --- a/chrome/browser/chromeos/offline/offline_load_page.h +++ b/chrome/browser/chromeos/offline/offline_load_page.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_PAGE_H_ #define CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_PAGE_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/offline/offline_load_page_unittest.cc b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc index a619243..9f5fcfb 100644 --- a/chrome/browser/chromeos/offline/offline_load_page_unittest.cc +++ b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc @@ -7,7 +7,9 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/offline/offline_load_page.h" #include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/browser/tab_contents/test_tab_contents.h" #include "chrome/common/render_messages.h" +#include "chrome/common/render_messages_params.h" static const char* kURL1 = "http://www.google.com/"; static const char* kURL2 = "http://www.gmail.com/"; diff --git a/chrome/browser/chromeos/offline/offline_load_service.cc b/chrome/browser/chromeos/offline/offline_load_service.cc index 63602f5..a000357 100644 --- a/chrome/browser/chromeos/offline/offline_load_service.cc +++ b/chrome/browser/chromeos/offline/offline_load_service.cc @@ -4,13 +4,13 @@ #include "chrome/browser/chromeos/offline/offline_load_service.h" -#include "base/singleton.h" #include "base/ref_counted.h" -#include "chrome/common/notification_service.h" +#include "base/singleton.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_util.h" -#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/common/notification_service.h" namespace chromeos { diff --git a/chrome/browser/chromeos/offline/offline_load_service.h b/chrome/browser/chromeos/offline/offline_load_service.h index 119475e..d052d51 100644 --- a/chrome/browser/chromeos/offline/offline_load_service.h +++ b/chrome/browser/chromeos/offline/offline_load_service.h @@ -4,13 +4,14 @@ #ifndef CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_SERVICE_H_ #define CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_SERVICE_H_ +#pragma once #include <tr1/unordered_set> #include "base/ref_counted.h" #include "chrome/common/notification_observer.h" -#include "chrome/common/notification_type.h" #include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_type.h" class GURL; class NavigationController; diff --git a/chrome/browser/chromeos/options/cellular_config_view.cc b/chrome/browser/chromeos/options/cellular_config_view.cc index be1e761..725efe8 100644 --- a/chrome/browser/chromeos/options/cellular_config_view.cc +++ b/chrome/browser/chromeos/options/cellular_config_view.cc @@ -5,23 +5,83 @@ #include "chrome/browser/chromeos/options/cellular_config_view.h" #include "app/l10n_util.h" +#include "base/i18n/time_formatting.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/options/network_config_view.h" +#include "chrome/common/time_format.h" #include "grit/generated_resources.h" +#include "views/controls/button/checkbox.h" +#include "views/controls/button/native_button.h" #include "views/controls/label.h" #include "views/grid_layout.h" #include "views/standard_layout.h" +namespace { + +enum PlanPurchaseType { + UNKNOWN, + NO_PURCHASE, // No purchase happened. + PURCHASED_DATA, // Purchased limited data plan. + PURCHASED_UNLIMITED_DATA // Purchased unlimited data plan. +}; + +struct PlanDetails { + PlanPurchaseType last_purchase_type; + base::Time last_purchase_time; + + int64 purchased_data; + base::TimeDelta purchased_time; + + int64 remaining_data; + base::TimeDelta remaining_time; +}; + +// TODO(xiyuan): Get real data from libcros when it's ready. +// Get plan details at the time being called. +void GetPlanDetails(const chromeos::CellularNetwork& cellular, + PlanDetails* details) { + // Free 5M 30day plan. + details->last_purchase_type = UNKNOWN; + details->last_purchase_time = base::Time::Now(); + details->purchased_data = 5 * 1024 * 1024; + details->purchased_time = base::TimeDelta::FromDays(30); + details->remaining_data = 2 * 1024 * 1024; + details->remaining_time = base::TimeDelta::FromDays(29); +} + +} // namespace + namespace chromeos { CellularConfigView::CellularConfigView(NetworkConfigView* parent, const CellularNetwork& cellular) : parent_(parent), cellular_(cellular), - autoconnect_checkbox_(NULL) { + purchase_info_(NULL), + purchase_more_button_(NULL), + remaining_data_info_(NULL), + expiration_info_(NULL), + show_notification_checkbox_(NULL), + autoconnect_checkbox_(NULL), + customer_support_link_(NULL) { Init(); } +void CellularConfigView::ButtonPressed(views::Button* button, + const views::Event& event) { + if (button == purchase_more_button_) { + // TODO(xiyuan): Purchase more... + } +} + +void CellularConfigView::LinkActivated(views::Link* source, int event_flags) { + if (source == customer_support_link_) { + // TODO(xiyuan): Find out where to go. + } +} + bool CellularConfigView::Save() { // Save auto-connect here. bool auto_connect = autoconnect_checkbox_->checked(); @@ -36,28 +96,128 @@ void CellularConfigView::Init() { views::GridLayout* layout = CreatePanelGridLayout(this); SetLayoutManager(layout); - int column_view_set_id = 0; - views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id); + purchase_info_ = new views::Label(); + purchase_more_button_ = new views::NativeButton(this, l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_MORE)); + views::Label* data_remaining_label = new views::Label(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_DATA_REMAINING)); + remaining_data_info_ = new views::Label(); + views::Label* expires_label = new views::Label(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_EXPIRES)); + expiration_info_ = new views::Label(); + show_notification_checkbox_ = new views::Checkbox(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SHOW_MOBILE_NOTIFICATION)); + autoconnect_checkbox_ = new views::Checkbox(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); + customer_support_link_ = new views::Link(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CUSTOMER_SUPPORT)); + + data_remaining_label->SetFont(data_remaining_label->font().DeriveFont(0, + gfx::Font::BOLD)); + expires_label->SetFont(data_remaining_label->font()); + + const int kColumnSetId = 0; + views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId); + column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); - column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); - layout->StartRow(0, column_view_set_id); - layout->AddView(new views::Label(l10n_util::GetString( - IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID))); - views::Label* label = new views::Label(ASCIIToWide(cellular_.name())); - label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - layout->AddView(label); + layout->StartRow(0, kColumnSetId); + layout->AddView(purchase_info_, 2, 1); + layout->AddView(purchase_more_button_); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - // Autoconnect checkbox - autoconnect_checkbox_ = new views::Checkbox( - l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); - autoconnect_checkbox_->SetChecked(cellular_.auto_connect()); - layout->StartRow(0, column_view_set_id); - layout->AddView(autoconnect_checkbox_, 2, 1); + layout->StartRow(0, kColumnSetId); + layout->AddView(data_remaining_label); + layout->AddView(remaining_data_info_, 2, 1); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + layout->StartRow(0, kColumnSetId); + layout->AddView(expires_label); + layout->AddView(expiration_info_, 2, 1); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + layout->StartRow(0, kColumnSetId); + layout->AddView(show_notification_checkbox_, 3, 1); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + layout->StartRow(0, kColumnSetId); + layout->AddView(autoconnect_checkbox_, 3, 1); + layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); + + layout->StartRow(0, kColumnSetId); + layout->AddView(customer_support_link_, 3, 1, + views::GridLayout::LEADING, views::GridLayout::TRAILING); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + Update(); +} + +void CellularConfigView::Update() { + autoconnect_checkbox_->SetChecked(cellular_.auto_connect()); + + PlanDetails details; + GetPlanDetails(cellular_, &details); + + switch (details.last_purchase_type) { + case NO_PURCHASE: + purchase_info_->SetText(l10n_util::GetStringF( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_RECEIVED_FREE_DATA, + UTF16ToWide(FormatBytes(details.purchased_data, + GetByteDisplayUnits(details.purchased_data), + true)), + base::TimeFormatFriendlyDate(details.last_purchase_time))); + remaining_data_info_->SetText( + UTF16ToWide(FormatBytes(details.remaining_data, + GetByteDisplayUnits(details.remaining_data), + true))); + expiration_info_->SetText(UTF16ToWide( + TimeFormat::TimeRemaining(details.remaining_time))); + break; + case PURCHASED_DATA: + purchase_info_->SetText(l10n_util::GetStringF( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_DATA, + UTF16ToWide(FormatBytes(details.purchased_data, + GetByteDisplayUnits(details.purchased_data), + true)), + base::TimeFormatFriendlyDate(details.last_purchase_time))); + remaining_data_info_->SetText( + UTF16ToWide(FormatBytes(details.remaining_data, + GetByteDisplayUnits(details.remaining_data), + true))); + expiration_info_->SetText(UTF16ToWide( + TimeFormat::TimeRemaining(details.remaining_time))); + break; + case PURCHASED_UNLIMITED_DATA: + purchase_info_->SetText(l10n_util::GetStringF( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_UNLIMITED_DATA, + UTF16ToWide(FormatBytes(details.purchased_data, + GetByteDisplayUnits(details.purchased_data), + true)), + base::TimeFormatFriendlyDate(details.last_purchase_time))); + remaining_data_info_->SetText(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_UNLIMITED)); + expiration_info_->SetText(UTF16ToWide( + TimeFormat::TimeRemaining(details.remaining_time))); + break; + case UNKNOWN: { + // TODO(xiyuan): Remove this when underlying data is provided. + const wchar_t kPlanType[] = L"Purchased plan: <Not yet implemented>"; + const wchar_t kNotImplemented[] = L"<Not yet implemented>"; + purchase_info_->SetText(kPlanType); + remaining_data_info_->SetText(kNotImplemented); + expiration_info_->SetText(kNotImplemented); + break; + } + default: + NOTREACHED() << "Unknown mobile plan purchase type."; + break; + } + + Layout(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/options/cellular_config_view.h b/chrome/browser/chromeos/options/cellular_config_view.h index f2ad38e..9120c6d 100644 --- a/chrome/browser/chromeos/options/cellular_config_view.h +++ b/chrome/browser/chromeos/options/cellular_config_view.h @@ -1,28 +1,42 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_OPTIONS_CELLULAR_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_CELLULAR_CONFIG_VIEW_H_ - -#include <string> +#pragma once #include "chrome/browser/chromeos/cros/network_library.h" -#include "views/controls/button/checkbox.h" +#include "views/controls/button/button.h" +#include "views/controls/link.h" #include "views/view.h" +namespace views { +class Checkbox; +class Label; +class NativeButton; +} // namespace views + namespace chromeos { class NetworkConfigView; // A dialog box for showing a password textfield. -class CellularConfigView : public views::View { +class CellularConfigView : public views::View, + public views::ButtonListener, + public views::LinkController { public: CellularConfigView(NetworkConfigView* parent, const CellularNetwork& cellular); explicit CellularConfigView(NetworkConfigView* parent); virtual ~CellularConfigView() {} + // views::ButtonListener implementation. + virtual void ButtonPressed(views::Button* button, const views::Event& event); + + // views::LinkController implementation. + virtual void LinkActivated(views::Link* source, int event_flags); + // Save network information. virtual bool Save(); @@ -31,11 +45,20 @@ class CellularConfigView : public views::View { // Initializes UI. void Init(); + // Updates UI. + void Update(); + NetworkConfigView* parent_; CellularNetwork cellular_; + views::Label* purchase_info_; + views::NativeButton* purchase_more_button_; + views::Label* remaining_data_info_; + views::Label* expiration_info_; + views::Checkbox* show_notification_checkbox_; views::Checkbox* autoconnect_checkbox_; + views::Link* customer_support_link_; DISALLOW_COPY_AND_ASSIGN(CellularConfigView); }; diff --git a/chrome/browser/chromeos/options/internet_page_view.cc b/chrome/browser/chromeos/options/internet_page_view.cc index 71f3e55..a095f3b 100644 --- a/chrome/browser/chromeos/options/internet_page_view.cc +++ b/chrome/browser/chromeos/options/internet_page_view.cc @@ -6,11 +6,14 @@ #include <string> -#include "app/combobox_model.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/options/network_config_view.h" #include "chrome/browser/chromeos/options/options_window_view.h" -#include "chrome/browser/chromeos/status/network_menu_button.h" +#include "chrome/browser/chromeos/status/network_menu.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/controls/button/native_button.h" @@ -246,7 +249,7 @@ void WiredSection::InitSection() { SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); if (!cros->ethernet_connecting() && !cros->ethernet_connected()) { - icon = NetworkMenuButton::IconForDisplay(icon, + icon = NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED)); } @@ -317,10 +320,10 @@ void WirelessSection::InitSection() { for (size_t i = 0; i < wifi_networks_.size(); ++i) { std::wstring name = ASCIIToWide(wifi_networks_[i].name()); - SkBitmap icon = NetworkMenuButton::IconForNetworkStrength( + SkBitmap icon = NetworkMenu::IconForNetworkStrength( wifi_networks_[i].strength(), true); if (wifi_networks_[i].encrypted()) { - icon = NetworkMenuButton::IconForDisplay(icon, + icon = NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } @@ -335,12 +338,12 @@ void WirelessSection::InitSection() { for (size_t i = 0; i < celluar_networks_.size(); ++i) { std::wstring name = ASCIIToWide(celluar_networks_[i].name()); - SkBitmap icon = NetworkMenuButton::IconForNetworkStrength( + SkBitmap icon = NetworkMenu::IconForNetworkStrength( celluar_networks_[i].strength(), true); // TODO(chocobo): Check cellular network 3g/edge. SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); // SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); - icon = NetworkMenuButton::IconForDisplay(icon, badge); + icon = NetworkMenu::IconForDisplay(icon, badge); bool connecting = celluar_networks_[i].connecting(); bool connected = celluar_networks_[i].connected(); @@ -446,7 +449,7 @@ void RememberedSection::InitSection() { SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); if (wifi_networks_[i].encrypted()) { - icon = NetworkMenuButton::IconForDisplay(icon, + icon = NetworkMenu::IconForDisplay(icon, *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); } @@ -463,7 +466,7 @@ void RememberedSection::InitSection() { // TODO(chocobo): Check cellular network 3g/edge. SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); // SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); - icon = NetworkMenuButton::IconForDisplay(icon, badge); + icon = NetworkMenu::IconForDisplay(icon, badge); AddNetwork(i, icon, name, false, std::wstring(), FORGET_BUTTON, TYPE_CELLULAR); @@ -474,12 +477,12 @@ void RememberedSection::ButtonClicked(int button, int connection_type, int id) { if (connection_type == TYPE_CELLULAR) { if (static_cast<int>(celluar_networks_.size()) > id) { CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - celluar_networks_[id]); + celluar_networks_[id].service_path()); } } else if (connection_type == TYPE_WIFI) { if (static_cast<int>(wifi_networks_.size()) > id) { CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - wifi_networks_[id]); + wifi_networks_[id].service_path()); } } else { NOTREACHED(); @@ -522,7 +525,7 @@ class InternetPageContentView : public SettingsPageView { InternetPageContentView::InternetPageContentView(Profile* profile) : SettingsPageView(profile) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - line_height_ = rb.GetFont(ResourceBundle::BaseFont).height(); + line_height_ = rb.GetFont(ResourceBundle::BaseFont).GetHeight(); } void InternetPageContentView::RefreshContents() { diff --git a/chrome/browser/chromeos/options/internet_page_view.h b/chrome/browser/chromeos/options/internet_page_view.h index 2b6f9ed..71b7399 100644 --- a/chrome/browser/chromeos/options/internet_page_view.h +++ b/chrome/browser/chromeos/options/internet_page_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_INTERNET_PAGE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_INTERNET_PAGE_VIEW_H_ +#pragma once #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/options/settings_page_view.h" @@ -25,7 +26,6 @@ class InternetPageView : public SettingsPageView, // NetworkLibrary::Observer implementation. virtual void NetworkChanged(NetworkLibrary* obj); - virtual void NetworkTraffic(NetworkLibrary* obj, int traffic_type) {} // views::View overrides: virtual void Layout(); diff --git a/chrome/browser/chromeos/options/ip_config_view.cc b/chrome/browser/chromeos/options/ip_config_view.cc index 9fb1520..8e596a2 100644 --- a/chrome/browser/chromeos/options/ip_config_view.cc +++ b/chrome/browser/chromeos/options/ip_config_view.cc @@ -6,6 +6,7 @@ #include "app/l10n_util.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" diff --git a/chrome/browser/chromeos/options/ip_config_view.h b/chrome/browser/chromeos/options/ip_config_view.h index f144666..c09fcc3 100644 --- a/chrome/browser/chromeos/options/ip_config_view.h +++ b/chrome/browser/chromeos/options/ip_config_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_IP_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_IP_CONFIG_VIEW_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/options/language_chewing_config_view.cc b/chrome/browser/chromeos/options/language_chewing_config_view.cc index 376be8f..40a597e 100644 --- a/chrome/browser/chromeos/options/language_chewing_config_view.cc +++ b/chrome/browser/chromeos/options/language_chewing_config_view.cc @@ -4,7 +4,6 @@ #include "chrome/browser/chromeos/options/language_chewing_config_view.h" -#include "app/combobox_model.h" #include "app/l10n_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" @@ -26,29 +25,34 @@ namespace chromeos { LanguageChewingConfigView::LanguageChewingConfigView(Profile* profile) : OptionsPageView(profile), contents_(NULL) { - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { chewing_boolean_prefs_[i].Init( - kChewingBooleanPrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kChewingBooleanPrefs[i].pref_name, + profile->GetPrefs(), this); chewing_boolean_checkboxes_[i] = NULL; } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { ChewingPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; current.multiple_choice_pref.Init( - kChewingMultipleChoicePrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kChewingMultipleChoicePrefs[i].pref_name, + profile->GetPrefs(), this); current.combobox_model = - new LanguageComboboxModel<const char*>(&kChewingMultipleChoicePrefs[i]); + new LanguageComboboxModel<const char*>( + &language_prefs::kChewingMultipleChoicePrefs[i]); current.combobox = NULL; } hsu_sel_key_type_.multiple_choice_pref.Init( - kChewingHsuSelKeyType.pref_name, profile->GetPrefs(), this); + language_prefs::kChewingHsuSelKeyType.pref_name, profile->GetPrefs(), + this); hsu_sel_key_type_.combobox_model = - new LanguageComboboxModel<int>(&kChewingHsuSelKeyType); + new LanguageComboboxModel<int>(&language_prefs::kChewingHsuSelKeyType); hsu_sel_key_type_.combobox = NULL; - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { chewing_integer_prefs_[i].Init( - kChewingIntegerPrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kChewingIntegerPrefs[i].pref_name, + profile->GetPrefs(), this); chewing_integer_sliders_[i] = NULL; } } @@ -60,13 +64,14 @@ void LanguageChewingConfigView::ButtonPressed( views::Button* sender, const views::Event& event) { views::Checkbox* checkbox = static_cast<views::Checkbox*>(sender); const int pref_id = checkbox->tag(); - DCHECK(pref_id >= 0 && pref_id < static_cast<int>(kNumChewingBooleanPrefs)); + DCHECK(pref_id >= 0 && pref_id < static_cast<int>( + language_prefs::kNumChewingBooleanPrefs)); chewing_boolean_prefs_[pref_id].SetValue(checkbox->checked()); } void LanguageChewingConfigView::ItemChanged( views::Combobox* sender, int prev_index, int new_index) { - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { ChewingPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; if (current.combobox == sender) { const std::string config_value = @@ -88,11 +93,12 @@ void LanguageChewingConfigView::ItemChanged( void LanguageChewingConfigView::SliderValueChanged(views::Slider* sender) { size_t pref_id; - for (pref_id = 0; pref_id < kNumChewingIntegerPrefs; ++pref_id) { + for (pref_id = 0; pref_id < language_prefs::kNumChewingIntegerPrefs; + ++pref_id) { if (chewing_integer_sliders_[pref_id] == sender) break; } - DCHECK(pref_id < kNumChewingIntegerPrefs); + DCHECK(pref_id < language_prefs::kNumChewingIntegerPrefs); chewing_integer_prefs_[pref_id].SetValue(sender->value()); } @@ -145,13 +151,14 @@ void LanguageChewingConfigView::InitControlLayout() { column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { chewing_boolean_checkboxes_[i] = new views::Checkbox( - l10n_util::GetString(kChewingBooleanPrefs[i].message_id)); + l10n_util::GetString( + language_prefs::kChewingBooleanPrefs[i].message_id)); chewing_boolean_checkboxes_[i]->set_listener(this); chewing_boolean_checkboxes_[i]->set_tag(i); } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { ChewingPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; current.combobox = new LanguageCombobox(current.combobox_model); current.combobox->set_listener(this); @@ -160,31 +167,32 @@ void LanguageChewingConfigView::InitControlLayout() { new LanguageCombobox(hsu_sel_key_type_.combobox_model); hsu_sel_key_type_.combobox->set_listener(this); - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { chewing_integer_sliders_[i] = new views::Slider( - kChewingIntegerPrefs[i].min_pref_value, - kChewingIntegerPrefs[i].max_pref_value, + language_prefs::kChewingIntegerPrefs[i].min_pref_value, + language_prefs::kChewingIntegerPrefs[i].max_pref_value, 1, static_cast<views::Slider::StyleFlags>( views::Slider::STYLE_DRAW_VALUE | views::Slider::STYLE_UPDATE_ON_RELEASE), this); } - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { layout->StartRow(0, kColumnSetId); layout->AddView(chewing_boolean_checkboxes_[i]); } - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { layout->StartRow(0, kColumnSetId); layout->AddView(new views::Label( - l10n_util::GetString(kChewingIntegerPrefs[i].message_id))); + l10n_util::GetString( + language_prefs::kChewingIntegerPrefs[i].message_id))); layout->AddView(chewing_integer_sliders_[i]); } NotifyPrefChanged(); // Show the comboboxes. - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { const ChewingPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; layout->StartRow(0, kColumnSetId); layout->AddView(new views::Label(current.combobox_model->GetLabel())); @@ -205,15 +213,15 @@ void LanguageChewingConfigView::Observe(NotificationType type, } void LanguageChewingConfigView::NotifyPrefChanged() { - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { const bool checked = chewing_boolean_prefs_[i].GetValue(); chewing_boolean_checkboxes_[i]->SetChecked(checked); } - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { const int value = chewing_integer_prefs_[i].GetValue(); chewing_integer_sliders_[i]->SetValue(value); } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { ChewingPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; const std::string value = current.multiple_choice_pref.GetValue(); for (int i = 0; i < current.combobox_model->num_items(); ++i) { diff --git a/chrome/browser/chromeos/options/language_chewing_config_view.h b/chrome/browser/chromeos/options/language_chewing_config_view.h index cdffac4..d7a33cf 100644 --- a/chrome/browser/chromeos/options/language_chewing_config_view.h +++ b/chrome/browser/chromeos/options/language_chewing_config_view.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CHEWING_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CHEWING_CONFIG_VIEW_H_ +#pragma once #include <string> #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/language_preferences.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/views/options/options_page_view.h" #include "views/controls/button/checkbox.h" #include "views/controls/combobox/combobox.h" @@ -69,20 +70,24 @@ class LanguageChewingConfigView : public views::ButtonListener, // Updates the chewing checkboxes. void NotifyPrefChanged(); - BooleanPrefMember chewing_boolean_prefs_[kNumChewingBooleanPrefs]; - IntegerPrefMember chewing_integer_prefs_[kNumChewingIntegerPrefs]; + BooleanPrefMember chewing_boolean_prefs_[ + language_prefs::kNumChewingBooleanPrefs]; + IntegerPrefMember chewing_integer_prefs_[ + language_prefs::kNumChewingIntegerPrefs]; views::View* contents_; // Checkboxes for Chewing. - views::Checkbox* chewing_boolean_checkboxes_[kNumChewingBooleanPrefs]; + views::Checkbox* chewing_boolean_checkboxes_[ + language_prefs::kNumChewingBooleanPrefs]; - views::Slider* chewing_integer_sliders_[kNumChewingIntegerPrefs]; + views::Slider* chewing_integer_sliders_[ + language_prefs::kNumChewingIntegerPrefs]; struct ChewingPrefAndAssociatedCombobox { StringPrefMember multiple_choice_pref; LanguageComboboxModel<const char*>* combobox_model; LanguageCombobox* combobox; - } prefs_and_comboboxes_[kNumChewingMultipleChoicePrefs]; + } prefs_and_comboboxes_[language_prefs::kNumChewingMultipleChoicePrefs]; struct HsuSelKeyTypePrefAndAssociatedCombobox { IntegerPrefMember multiple_choice_pref; diff --git a/chrome/browser/chromeos/options/language_config_model.cc b/chrome/browser/chromeos/options/language_config_model.cc index fafa2f6..3502c2f 100644 --- a/chrome/browser/chromeos/options/language_config_model.cc +++ b/chrome/browser/chromeos/options/language_config_model.cc @@ -9,6 +9,7 @@ #include <utility> #include "app/l10n_util.h" +#include "base/string_split.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/input_method_library.h" @@ -16,6 +17,7 @@ #include "chrome/browser/chromeos/preferences.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" namespace chromeos { @@ -30,14 +32,13 @@ int AddLanguageComboboxModel::GetItemCount() { return get_languages_count() + 1 - ignore_set_.size(); } -std::wstring AddLanguageComboboxModel::GetItemAt(int index) { +string16 AddLanguageComboboxModel::GetItemAt(int index) { // Show "Add language" as the first item. if (index == 0) { - return l10n_util::GetString( + return l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_COMBOBOX); } - return input_method::MaybeRewriteLanguageName( - GetLanguageNameAt(GetLanguageIndex(index))); + return WideToUTF16Hack(GetLanguageNameAt(GetLanguageIndex(index))); } int AddLanguageComboboxModel::GetLanguageIndex(int index) const { @@ -213,10 +214,6 @@ void LanguageConfigModel::GetInputMethodIdsFromLanguageCode( input_method_ids->clear(); input_method::GetInputMethodIdsFromLanguageCode( language_code, input_method::kAllInputMethods, input_method_ids); - - // Reorder the input methods. - input_method::ReorderInputMethodIdsForLanguageCode( - language_code, input_method_ids); } void LanguageConfigModel::NotifyPrefChanged() { diff --git a/chrome/browser/chromeos/options/language_config_model.h b/chrome/browser/chromeos/options/language_config_model.h index 352208c..05a63ac 100644 --- a/chrome/browser/chromeos/options/language_config_model.h +++ b/chrome/browser/chromeos/options/language_config_model.h @@ -4,15 +4,16 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_MODEL_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_MODEL_H_ +#pragma once -#include <map> #include <set> #include <string> #include <vector> +#include "base/string16.h" #include "chrome/browser/language_combobox_model.h" -#include "chrome/browser/pref_member.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" #include "cros/chromeos_input_method.h" @@ -21,13 +22,13 @@ namespace chromeos { // The combobox model is used for adding languages in the language config // view. -class AddLanguageComboboxModel : public LanguageComboboxModel { +class AddLanguageComboboxModel : public ::LanguageComboboxModel { public: AddLanguageComboboxModel(Profile* profile, const std::vector<std::string>& locale_codes); // LanguageComboboxModel overrides. virtual int GetItemCount(); - virtual std::wstring GetItemAt(int index); + virtual string16 GetItemAt(int index); // Converts the given index (index of the items in the combobox) to the // index of the internal language list. The returned index can be used diff --git a/chrome/browser/chromeos/options/language_config_model_unittest.cc b/chrome/browser/chromeos/options/language_config_model_unittest.cc index e9b593d..0d7e12f 100644 --- a/chrome/browser/chromeos/options/language_config_model_unittest.cc +++ b/chrome/browser/chromeos/options/language_config_model_unittest.cc @@ -26,18 +26,18 @@ TEST(AddLanguageComboboxModelTest, AddLanguageComboboxModel) { ASSERT_EQ(4, model.GetItemCount()); // The first item should be "Add language" labe. - EXPECT_EQ(l10n_util::GetString( + EXPECT_EQ(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_COMBOBOX), model.GetItemAt(0)); // Other items should be sorted language display names for UI (hence // French comes before German). Note that the returned display names // are followed by their native representations. To simplify matching, // use StartsWith() here. - EXPECT_TRUE(StartsWith(model.GetItemAt(1), L"French", true)) + EXPECT_TRUE(StartsWith(model.GetItemAt(1), ASCIIToUTF16("French"), true)) << model.GetItemAt(1); - EXPECT_TRUE(StartsWith(model.GetItemAt(2), L"German", true)) + EXPECT_TRUE(StartsWith(model.GetItemAt(2), ASCIIToUTF16("German"), true)) << model.GetItemAt(2); - EXPECT_TRUE(StartsWith(model.GetItemAt(3), L"Korean", true)) + EXPECT_TRUE(StartsWith(model.GetItemAt(3), ASCIIToUTF16("Korean"), true)) << model.GetItemAt(3); // GetLanguageIndex() returns the given index -1 to offset "Add language". diff --git a/chrome/browser/chromeos/options/language_config_util.h b/chrome/browser/chromeos/options/language_config_util.h index cc80ee7..6f262cf 100644 --- a/chrome/browser/chromeos/options/language_config_util.h +++ b/chrome/browser/chromeos/options/language_config_util.h @@ -4,7 +4,10 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_UTIL_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_UTIL_H_ +#pragma once +#include "app/combobox_model.h" +#include "base/string16.h" #include "chrome/browser/chromeos/language_preferences.h" #include "views/controls/combobox/combobox.h" @@ -15,12 +18,14 @@ template <typename DataType> class LanguageComboboxModel : public ComboboxModel { public: explicit LanguageComboboxModel( - const LanguageMultipleChoicePreference<DataType>* pref_data) + const language_prefs::LanguageMultipleChoicePreference<DataType>* + pref_data) : pref_data_(pref_data), num_items_(0) { // Check how many items are defined in the |pref_data->values_and_ids| // array. for (size_t i = 0; - i < LanguageMultipleChoicePreference<DataType>::kMaxItems; ++i) { + i < language_prefs::LanguageMultipleChoicePreference<DataType>:: + kMaxItems; ++i) { if ((pref_data_->values_and_ids)[i].item_message_id == 0) { break; } @@ -34,13 +39,13 @@ class LanguageComboboxModel : public ComboboxModel { } // Implements ComboboxModel interface. - virtual std::wstring GetItemAt(int index) { + virtual string16 GetItemAt(int index) { if (index < 0 || index >= num_items_) { LOG(ERROR) << "Index is out of bounds: " << index; - return L""; + return string16(); } const int message_id = (pref_data_->values_and_ids)[index].item_message_id; - return l10n_util::GetString(message_id); + return l10n_util::GetStringUTF16(message_id); } // Gets a label for the combobox like "Input mode". This function is NOT part @@ -65,7 +70,7 @@ class LanguageComboboxModel : public ComboboxModel { } private: - const LanguageMultipleChoicePreference<DataType>* pref_data_; + const language_prefs::LanguageMultipleChoicePreference<DataType>* pref_data_; int num_items_; DISALLOW_COPY_AND_ASSIGN(LanguageComboboxModel); diff --git a/chrome/browser/chromeos/options/language_config_view.cc b/chrome/browser/chromeos/options/language_config_view.cc index d9f482e..1ee7845 100644 --- a/chrome/browser/chromeos/options/language_config_view.cc +++ b/chrome/browser/chromeos/options/language_config_view.cc @@ -17,7 +17,7 @@ #include "chrome/browser/chromeos/options/options_window_view.h" #include "chrome/browser/chromeos/preferences.h" #include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/views/restart_message_box.h" #include "chrome/common/notification_type.h" @@ -515,8 +515,7 @@ void LanguageConfigView::AddUiLanguageSection(const std::string& language_code, g_browser_process->GetApplicationLocale(); const string16 language_name16 = l10n_util::GetDisplayNameForLocale( language_code, application_locale, true); - const std::wstring language_name - = input_method::MaybeRewriteLanguageName(UTF16ToWide(language_name16)); + const std::wstring language_name = UTF16ToWide(language_name16); views::Label* language_name_label = new views::Label(language_name); language_name_label->SetFont( language_name_label->font().DeriveFont(0, gfx::Font::BOLD)); diff --git a/chrome/browser/chromeos/options/language_config_view.h b/chrome/browser/chromeos/options/language_config_view.h index bd2b0e3..a7dd980 100644 --- a/chrome/browser/chromeos/options/language_config_view.h +++ b/chrome/browser/chromeos/options/language_config_view.h @@ -4,11 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_VIEW_H_ +#pragma once #include <map> #include <set> #include <string> -#include <vector> #include "app/table_model.h" #include "chrome/browser/chromeos/options/language_config_model.h" diff --git a/chrome/browser/chromeos/options/language_hangul_config_view.cc b/chrome/browser/chromeos/options/language_hangul_config_view.cc index 9158c89..0e8227d 100644 --- a/chrome/browser/chromeos/options/language_hangul_config_view.cc +++ b/chrome/browser/chromeos/options/language_hangul_config_view.cc @@ -6,14 +6,15 @@ #include "app/combobox_model.h" #include "app/l10n_util.h" +#include "base/string16.h" #include "base/utf_string_conversions.h" -#include "chrome/common/notification_type.h" -#include "chrome/common/pref_names.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/preferences.h" #include "chrome/browser/profile.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "views/controls/button/checkbox.h" @@ -28,10 +29,12 @@ namespace chromeos { class HangulKeyboardComboboxModel : public ComboboxModel { public: HangulKeyboardComboboxModel() { - for (size_t i = 0; i < arraysize(kHangulKeyboardNameIDPairs); ++i) { + for (size_t i = 0; i < language_prefs::kNumHangulKeyboardNameIDPairs; + ++i) { layouts_.push_back(std::make_pair( - l10n_util::GetStringUTF8(kHangulKeyboardNameIDPairs[i].message_id), - kHangulKeyboardNameIDPairs[i].keyboard_id)); + l10n_util::GetStringUTF8( + language_prefs::kHangulKeyboardNameIDPairs[i].message_id), + language_prefs::kHangulKeyboardNameIDPairs[i].keyboard_id)); } } @@ -41,12 +44,12 @@ class HangulKeyboardComboboxModel : public ComboboxModel { } // Implements ComboboxModel interface. - virtual std::wstring GetItemAt(int index) { + virtual string16 GetItemAt(int index) { if (index < 0 || index > GetItemCount()) { LOG(ERROR) << "Index is out of bounds: " << index; - return L""; + return string16(); } - return UTF8ToWide(layouts_.at(index).first); + return UTF8ToUTF16(layouts_.at(index).first); } // Gets a keyboard layout ID (e.g. "2", "3f", ..) for an item at zero-origin diff --git a/chrome/browser/chromeos/options/language_hangul_config_view.h b/chrome/browser/chromeos/options/language_hangul_config_view.h index 88f3188..58466b0 100644 --- a/chrome/browser/chromeos/options/language_hangul_config_view.h +++ b/chrome/browser/chromeos/options/language_hangul_config_view.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_HANGUL_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_HANGUL_CONFIG_VIEW_H_ +#pragma once #include <string> #include "base/scoped_ptr.h" #include "chrome/browser/chromeos/cros/input_method_library.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/views/options/options_page_view.h" #include "views/controls/combobox/combobox.h" #include "views/controls/label.h" diff --git a/chrome/browser/chromeos/options/language_mozc_config_view.cc b/chrome/browser/chromeos/options/language_mozc_config_view.cc index a48f5ec..15fbfdf 100644 --- a/chrome/browser/chromeos/options/language_mozc_config_view.cc +++ b/chrome/browser/chromeos/options/language_mozc_config_view.cc @@ -4,7 +4,6 @@ #include "chrome/browser/chromeos/options/language_mozc_config_view.h" -#include "app/combobox_model.h" #include "app/l10n_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" @@ -26,7 +25,7 @@ namespace { enum ButtonTag { // 0 to kNumMozcBooleanPrefs - 1 are reserved for the checkboxes for integer // preferences. - kResetToDefaultsButton = chromeos::kNumMozcBooleanPrefs, + kResetToDefaultsButton = chromeos::language_prefs::kNumMozcBooleanPrefs, }; } // namespace @@ -36,30 +35,34 @@ LanguageMozcConfigView::LanguageMozcConfigView(Profile* profile) : OptionsPageView(profile), contents_(NULL), reset_to_defaults_button_(NULL) { - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { MozcPrefAndAssociatedCheckbox& current = prefs_and_checkboxes_[i]; current.boolean_pref.Init( - kMozcBooleanPrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kMozcBooleanPrefs[i].pref_name, profile->GetPrefs(), + this); current.checkbox = NULL; } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { MozcPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; current.multiple_choice_pref.Init( - kMozcMultipleChoicePrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kMozcMultipleChoicePrefs[i].pref_name, + profile->GetPrefs(), this); current.combobox_model = - new LanguageComboboxModel<const char*>(&kMozcMultipleChoicePrefs[i]); + new LanguageComboboxModel<const char*>( + &language_prefs::kMozcMultipleChoicePrefs[i]); current.combobox = NULL; } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { MozcPrefAndAssociatedSlider& current = prefs_and_sliders_[i]; current.integer_pref.Init( - kMozcIntegerPrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kMozcIntegerPrefs[i].pref_name, profile->GetPrefs(), + this); current.slider = NULL; } } LanguageMozcConfigView::~LanguageMozcConfigView() { - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { delete prefs_and_comboboxes_[i].combobox_model; } } @@ -72,7 +75,8 @@ void LanguageMozcConfigView::ButtonPressed( return; } views::Checkbox* checkbox = static_cast<views::Checkbox*>(sender); - DCHECK(pref_id >= 0 && pref_id < static_cast<int>(kNumMozcBooleanPrefs)); + DCHECK(pref_id >= 0 && pref_id < static_cast<int>( + language_prefs::kNumMozcBooleanPrefs)); prefs_and_checkboxes_[pref_id].boolean_pref.SetValue(checkbox->checked()); } @@ -82,7 +86,7 @@ void LanguageMozcConfigView::ItemChanged( LOG(ERROR) << "Invalid new_index: " << new_index; return; } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { MozcPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; if (current.combobox == sender) { const std::string config_value = @@ -97,11 +101,12 @@ void LanguageMozcConfigView::ItemChanged( void LanguageMozcConfigView::SliderValueChanged(views::Slider* sender) { size_t pref_id; - for (pref_id = 0; pref_id < kNumMozcIntegerPrefs; ++pref_id) { + for (pref_id = 0; pref_id < language_prefs::kNumMozcIntegerPrefs; + ++pref_id) { if (prefs_and_sliders_[pref_id].slider == sender) break; } - DCHECK(pref_id < kNumMozcIntegerPrefs); + DCHECK(pref_id < language_prefs::kNumMozcIntegerPrefs); prefs_and_sliders_[pref_id].integer_pref.SetValue(sender->value()); } @@ -159,23 +164,23 @@ void LanguageMozcConfigView::InitControlLayout() { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { MozcPrefAndAssociatedCheckbox& current = prefs_and_checkboxes_[i]; current.checkbox = new views::Checkbox( - l10n_util::GetString(kMozcBooleanPrefs[i].message_id)); + l10n_util::GetString(language_prefs::kMozcBooleanPrefs[i].message_id)); current.checkbox->set_listener(this); current.checkbox->set_tag(i); } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { MozcPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; current.combobox = new LanguageCombobox(current.combobox_model); current.combobox->set_listener(this); } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { MozcPrefAndAssociatedSlider& current = prefs_and_sliders_[i]; current.slider = new views::Slider( - kMozcIntegerPrefs[i].min_pref_value, - kMozcIntegerPrefs[i].max_pref_value, + language_prefs::kMozcIntegerPrefs[i].min_pref_value, + language_prefs::kMozcIntegerPrefs[i].max_pref_value, 1, static_cast<views::Slider::StyleFlags>( views::Slider::STYLE_DRAW_VALUE | @@ -192,23 +197,24 @@ void LanguageMozcConfigView::InitControlLayout() { layout->AddView(reset_to_defaults_button_); // Show the checkboxes. - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { const MozcPrefAndAssociatedCheckbox& current = prefs_and_checkboxes_[i]; layout->StartRow(0, kColumnSetId); layout->AddView(current.checkbox, 3, 1); } // Show the comboboxes. - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { const MozcPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; layout->StartRow(0, kColumnSetId); layout->AddView(new views::Label(current.combobox_model->GetLabel())); layout->AddView(current.combobox); } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { const MozcPrefAndAssociatedSlider& current = prefs_and_sliders_[i]; layout->StartRow(0, kColumnSetId); layout->AddView(new views::Label( - l10n_util::GetString(kMozcIntegerPrefs[i].message_id))); + l10n_util::GetString( + language_prefs::kMozcIntegerPrefs[i].message_id))); layout->AddView(current.slider); } NotifyPrefChanged(); // Sync the slider with current Chrome prefs. @@ -225,12 +231,12 @@ void LanguageMozcConfigView::Observe(NotificationType type, void LanguageMozcConfigView::NotifyPrefChanged() { // Update comboboxes. // TODO(yusukes): We don't have to update all UI controls. - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { MozcPrefAndAssociatedCheckbox& current = prefs_and_checkboxes_[i]; const bool checked = current.boolean_pref.GetValue(); current.checkbox->SetChecked(checked); } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { MozcPrefAndAssociatedCombobox& current = prefs_and_comboboxes_[i]; const std::string value = current.multiple_choice_pref.GetValue(); for (int i = 0; i < current.combobox_model->num_items(); ++i) { @@ -240,7 +246,7 @@ void LanguageMozcConfigView::NotifyPrefChanged() { } } } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { MozcPrefAndAssociatedSlider& current = prefs_and_sliders_[i]; const int value = current.integer_pref.GetValue(); current.slider->SetValue(value); @@ -248,17 +254,17 @@ void LanguageMozcConfigView::NotifyPrefChanged() { } void LanguageMozcConfigView::ResetToDefaults() { - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { prefs_and_checkboxes_[i].boolean_pref.SetValue( - kMozcBooleanPrefs[i].default_pref_value); + language_prefs::kMozcBooleanPrefs[i].default_pref_value); } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { prefs_and_comboboxes_[i].multiple_choice_pref.SetValue( - kMozcMultipleChoicePrefs[i].default_pref_value); + language_prefs::kMozcMultipleChoicePrefs[i].default_pref_value); } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { prefs_and_sliders_[i].integer_pref.SetValue( - kMozcIntegerPrefs[i].default_pref_value); + language_prefs::kMozcIntegerPrefs[i].default_pref_value); } // Reflect the preference changes to the controls. NotifyPrefChanged(); diff --git a/chrome/browser/chromeos/options/language_mozc_config_view.h b/chrome/browser/chromeos/options/language_mozc_config_view.h index 0ee0266..496ec14 100644 --- a/chrome/browser/chromeos/options/language_mozc_config_view.h +++ b/chrome/browser/chromeos/options/language_mozc_config_view.h @@ -4,13 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_MOZC_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_MOZC_CONFIG_VIEW_H_ +#pragma once #include <string> -#include "base/scoped_ptr.h" #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/language_preferences.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/views/options/options_page_view.h" #include "views/controls/combobox/combobox.h" #include "views/controls/label.h" @@ -83,18 +83,18 @@ class LanguageMozcConfigView : public views::ButtonListener, struct MozcPrefAndAssociatedCheckbox { BooleanPrefMember boolean_pref; views::Checkbox* checkbox; - } prefs_and_checkboxes_[kNumMozcBooleanPrefs]; + } prefs_and_checkboxes_[language_prefs::kNumMozcBooleanPrefs]; struct MozcPrefAndAssociatedCombobox { StringPrefMember multiple_choice_pref; LanguageComboboxModel<const char*>* combobox_model; LanguageCombobox* combobox; - } prefs_and_comboboxes_[kNumMozcMultipleChoicePrefs]; + } prefs_and_comboboxes_[language_prefs::kNumMozcMultipleChoicePrefs]; struct MozcPrefAndAssociatedSlider { IntegerPrefMember integer_pref; views::Slider* slider; - } prefs_and_sliders_[kNumMozcIntegerPrefs]; + } prefs_and_sliders_[language_prefs::kNumMozcIntegerPrefs]; DISALLOW_COPY_AND_ASSIGN(LanguageMozcConfigView); }; diff --git a/chrome/browser/chromeos/options/language_pinyin_config_view.cc b/chrome/browser/chromeos/options/language_pinyin_config_view.cc index 48d79d7..2290f4e 100644 --- a/chrome/browser/chromeos/options/language_pinyin_config_view.cc +++ b/chrome/browser/chromeos/options/language_pinyin_config_view.cc @@ -4,16 +4,15 @@ #include "chrome/browser/chromeos/options/language_pinyin_config_view.h" -#include "app/combobox_model.h" #include "app/l10n_util.h" #include "base/utf_string_conversions.h" -#include "chrome/common/notification_type.h" -#include "chrome/common/pref_names.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/options/language_config_util.h" #include "chrome/browser/chromeos/preferences.h" #include "chrome/browser/profile.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "views/controls/button/checkbox.h" @@ -26,16 +25,19 @@ namespace chromeos { LanguagePinyinConfigView::LanguagePinyinConfigView(Profile* profile) : OptionsPageView(profile), contents_(NULL) { - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { pinyin_boolean_prefs_[i].Init( - kPinyinBooleanPrefs[i].pref_name, profile->GetPrefs(), this); + language_prefs::kPinyinBooleanPrefs[i].pref_name, profile->GetPrefs(), + this); pinyin_boolean_checkboxes_[i] = NULL; } double_pinyin_schema_.multiple_choice_pref.Init( - kPinyinDoublePinyinSchema.pref_name, profile->GetPrefs(), this); + language_prefs::kPinyinDoublePinyinSchema.pref_name, + profile->GetPrefs(), this); double_pinyin_schema_.combobox_model = - new LanguageComboboxModel<int>(&kPinyinDoublePinyinSchema); + new LanguageComboboxModel<int>( + &language_prefs::kPinyinDoublePinyinSchema); double_pinyin_schema_.combobox = NULL; } @@ -46,7 +48,8 @@ void LanguagePinyinConfigView::ButtonPressed( views::Button* sender, const views::Event& event) { views::Checkbox* checkbox = static_cast<views::Checkbox*>(sender); const int pref_id = checkbox->tag(); - DCHECK(pref_id >= 0 && pref_id < static_cast<int>(kNumPinyinBooleanPrefs)); + DCHECK(pref_id >= 0 && pref_id < static_cast<int>( + language_prefs::kNumPinyinBooleanPrefs)); pinyin_boolean_prefs_[pref_id].SetValue(checkbox->checked()); } @@ -110,9 +113,10 @@ void LanguagePinyinConfigView::InitControlLayout() { column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { pinyin_boolean_checkboxes_[i] = new views::Checkbox( - l10n_util::GetString(kPinyinBooleanPrefs[i].message_id)); + l10n_util::GetString( + language_prefs::kPinyinBooleanPrefs[i].message_id)); pinyin_boolean_checkboxes_[i]->set_listener(this); pinyin_boolean_checkboxes_[i]->set_tag(i); } @@ -121,7 +125,7 @@ void LanguagePinyinConfigView::InitControlLayout() { double_pinyin_schema_.combobox->set_listener(this); NotifyPrefChanged(); - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { layout->StartRow(0, kColumnSetId); layout->AddView(pinyin_boolean_checkboxes_[i]); } @@ -140,7 +144,7 @@ void LanguagePinyinConfigView::Observe(NotificationType type, } void LanguagePinyinConfigView::NotifyPrefChanged() { - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { const bool checked = pinyin_boolean_prefs_[i].GetValue(); pinyin_boolean_checkboxes_[i]->SetChecked(checked); } diff --git a/chrome/browser/chromeos/options/language_pinyin_config_view.h b/chrome/browser/chromeos/options/language_pinyin_config_view.h index 9879f28..6d38bd5 100644 --- a/chrome/browser/chromeos/options/language_pinyin_config_view.h +++ b/chrome/browser/chromeos/options/language_pinyin_config_view.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_PINYIN_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_PINYIN_CONFIG_VIEW_H_ +#pragma once #include <string> #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/language_preferences.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/views/options/options_page_view.h" #include "views/controls/button/checkbox.h" #include "views/controls/combobox/combobox.h" @@ -64,12 +65,14 @@ class LanguagePinyinConfigView : public views::ButtonListener, // Updates the pinyin checkboxes. void NotifyPrefChanged(); - BooleanPrefMember pinyin_boolean_prefs_[kNumPinyinBooleanPrefs]; + BooleanPrefMember pinyin_boolean_prefs_[ + language_prefs::kNumPinyinBooleanPrefs]; // TODO(yusukes): Support integer prefs if needed. views::View* contents_; // A checkboxes for Pinyin. - views::Checkbox* pinyin_boolean_checkboxes_[kNumPinyinBooleanPrefs]; + views::Checkbox* pinyin_boolean_checkboxes_[ + language_prefs::kNumPinyinBooleanPrefs]; struct DoublePinyinSchemaPrefAndAssociatedCombobox { IntegerPrefMember multiple_choice_pref; diff --git a/chrome/browser/chromeos/options/network_config_view.cc b/chrome/browser/chromeos/options/network_config_view.cc index b139b56..d18006e 100644 --- a/chrome/browser/chromeos/options/network_config_view.cc +++ b/chrome/browser/chromeos/options/network_config_view.cc @@ -6,6 +6,7 @@ #include "app/l10n_util.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/options/cellular_config_view.h" #include "chrome/browser/chromeos/options/ip_config_view.h" #include "chrome/browser/chromeos/options/wifi_config_view.h" @@ -178,7 +179,7 @@ void NetworkConfigView::Init() { if (flags_ & FLAG_CELLULAR) { cellularconfig_view_ = new CellularConfigView(this, cellular_); tabs_->AddTab( - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_NETWORK_CONFIG), + l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_USAGE), cellularconfig_view_); } if (flags_ & FLAG_WIFI) { diff --git a/chrome/browser/chromeos/options/network_config_view.h b/chrome/browser/chromeos/options/network_config_view.h index 2bdd392..2fda171 100644 --- a/chrome/browser/chromeos/options/network_config_view.h +++ b/chrome/browser/chromeos/options/network_config_view.h @@ -1,13 +1,13 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_OPTIONS_NETWORK_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_NETWORK_CONFIG_VIEW_H_ +#pragma once #include <string> -#include "base/string16.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "views/controls/tabbed_pane/tabbed_pane.h" #include "views/window/dialog_delegate.h" diff --git a/chrome/browser/chromeos/options/options_window_view.cc b/chrome/browser/chromeos/options/options_window_view.cc index 4535e8b..b9602f2 100644 --- a/chrome/browser/chromeos/options/options_window_view.cc +++ b/chrome/browser/chromeos/options/options_window_view.cc @@ -5,7 +5,6 @@ #include "chrome/browser/options_window.h" #include "app/l10n_util.h" -#include "app/resource_bundle.h" #include "base/scoped_ptr.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" @@ -15,8 +14,9 @@ #include "chrome/browser/gtk/options/advanced_page_gtk.h" #include "chrome/browser/gtk/options/content_page_gtk.h" #include "chrome/browser/gtk/options/general_page_gtk.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/views/accessible_view_helper.h" #include "chrome/browser/window_sizer.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/pref_names.h" @@ -143,6 +143,8 @@ class OptionsWindowView : public views::View, // The last page the user was on when they opened the Options window. IntegerPrefMember last_selected_page_; + scoped_ptr<AccessibleViewHelper> accessible_view_helper_; + DISALLOW_IMPLICIT_CONSTRUCTORS(OptionsWindowView); }; @@ -253,6 +255,9 @@ void OptionsWindowView::Layout() { tabs_->SetBounds(kDialogPadding, kDialogPadding, width() - (2 * kDialogPadding), height() - (2 * kDialogPadding)); + if (!accessible_view_helper_.get()) { + accessible_view_helper_.reset(new AccessibleViewHelper(this, profile_)); + } } gfx::Size OptionsWindowView::GetPreferredSize() { diff --git a/chrome/browser/chromeos/options/options_window_view.h b/chrome/browser/chromeos/options/options_window_view.h index a6b31b6..784ec8d 100644 --- a/chrome/browser/chromeos/options/options_window_view.h +++ b/chrome/browser/chromeos/options/options_window_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_OPTIONS_WINDOW_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_OPTIONS_WINDOW_VIEW_H_ +#pragma once namespace chromeos { diff --git a/chrome/browser/chromeos/options/settings_page_view.cc b/chrome/browser/chromeos/options/settings_page_view.cc index e6afc30..84d9654 100644 --- a/chrome/browser/chromeos/options/settings_page_view.cc +++ b/chrome/browser/chromeos/options/settings_page_view.cc @@ -4,6 +4,9 @@ #include "chrome/browser/chromeos/options/settings_page_view.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/string_util.h" #include "gfx/skia_utils_gtk.h" #include "views/controls/label.h" #include "views/fill_layout.h" diff --git a/chrome/browser/chromeos/options/settings_page_view.h b/chrome/browser/chromeos/options/settings_page_view.h index 5793980..35e9934 100644 --- a/chrome/browser/chromeos/options/settings_page_view.h +++ b/chrome/browser/chromeos/options/settings_page_view.h @@ -4,13 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_SETTINGS_PAGE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_SETTINGS_PAGE_VIEW_H_ +#pragma once #include <gtk/gtk.h> #include "chrome/browser/views/options/options_page_view.h" -#include "app/l10n_util.h" -#include "app/resource_bundle.h" #include "views/grid_layout.h" #include "views/standard_layout.h" diff --git a/chrome/browser/chromeos/options/system_page_view.cc b/chrome/browser/chromeos/options/system_page_view.cc index f074889..3cd9d40 100644 --- a/chrome/browser/chromeos/options/system_page_view.cc +++ b/chrome/browser/chromeos/options/system_page_view.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -8,13 +8,20 @@ #include <vector> #include "app/combobox_model.h" +#include "app/l10n_util.h" #include "base/stl_util-inl.h" +#include "base/string16.h" +#include "base/string_util.h" +#include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/keyboard_library.h" #include "chrome/browser/chromeos/cros/system_library.h" +#include "chrome/browser/chromeos/language_preferences.h" +#include "chrome/browser/chromeos/options/language_config_util.h" #include "chrome/browser/chromeos/options/language_config_view.h" #include "chrome/browser/chromeos/options/options_window_view.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" #include "grit/generated_resources.h" @@ -121,14 +128,15 @@ class DateTimeSection : public SettingsPageSection, return static_cast<int>(timezones_.size()); } - virtual std::wstring GetItemAt(int index) { + virtual string16 GetItemAt(int index) { icu::UnicodeString name; timezones_[index]->getDisplayName(name); std::wstring output; UTF16ToWide(name.getBuffer(), name.length(), &output); int hour_offset = timezones_[index]->getRawOffset() / 3600000; - return StringPrintf(hour_offset == 0 ? L"(GMT) " : (hour_offset > 0 ? - L"(GMT+%d) " : L"(GMT%d) "), hour_offset) + output; + return WideToUTF16Hack( + base::StringPrintf(hour_offset == 0 ? L"(GMT) " : (hour_offset > 0 ? + L"(GMT+%d) " : L"(GMT%d) "), hour_offset) + output); } virtual icu::TimeZone* GetTimeZoneAt(int index) { @@ -211,7 +219,7 @@ class TouchpadSection : public SettingsPageSection, protected: // SettingsPageSection overrides: virtual void InitContents(GridLayout* layout); - virtual void NotifyPrefChanged(const std::wstring* pref_name); + virtual void NotifyPrefChanged(const std::string* pref_name); private: // The View that contains the contents of the section. @@ -219,14 +227,10 @@ class TouchpadSection : public SettingsPageSection, // Controls for this section: views::Checkbox* enable_tap_to_click_checkbox_; - views::Checkbox* enable_vert_edge_scroll_checkbox_; - views::Slider* speed_factor_slider_; views::Slider* sensitivity_slider_; // Preferences for this section: BooleanPrefMember tap_to_click_enabled_; - BooleanPrefMember vert_edge_scroll_enabled_; - IntegerPrefMember speed_factor_; IntegerPrefMember sensitivity_; DISALLOW_COPY_AND_ASSIGN(TouchpadSection); @@ -235,8 +239,6 @@ class TouchpadSection : public SettingsPageSection, TouchpadSection::TouchpadSection(Profile* profile) : SettingsPageSection(profile, IDS_OPTIONS_SETTINGS_SECTION_TITLE_TOUCHPAD), enable_tap_to_click_checkbox_(NULL), - enable_vert_edge_scroll_checkbox_(NULL), - speed_factor_slider_(NULL), sensitivity_slider_(NULL) { } @@ -249,24 +251,11 @@ void TouchpadSection::ButtonPressed( UserMetricsAction("Options_TapToClickCheckbox_Disable"), profile()->GetPrefs()); tap_to_click_enabled_.SetValue(enabled); - } else if (sender == enable_vert_edge_scroll_checkbox_) { - bool enabled = enable_vert_edge_scroll_checkbox_->checked(); - UserMetricsRecordAction(enabled ? - UserMetricsAction("Options_VertEdgeScrollCheckbox_Enable") : - UserMetricsAction("Options_VertEdgeScrollCheckbox_Disable"), - profile()->GetPrefs()); - vert_edge_scroll_enabled_.SetValue(enabled); } } void TouchpadSection::SliderValueChanged(views::Slider* sender) { - if (sender == speed_factor_slider_) { - double value = speed_factor_slider_->value(); - UserMetricsRecordAction( - UserMetricsAction("Options_SpeedFactorSlider_Changed"), - profile()->GetPrefs()); - speed_factor_.SetValue(value); - } else if (sender == sensitivity_slider_) { + if (sender == sensitivity_slider_) { double value = sensitivity_slider_->value(); UserMetricsRecordAction( UserMetricsAction("Options_SensitivitySlider_Changed"), @@ -280,18 +269,8 @@ void TouchpadSection::InitContents(GridLayout* layout) { IDS_OPTIONS_SETTINGS_TAP_TO_CLICK_ENABLED_DESCRIPTION)); enable_tap_to_click_checkbox_->set_listener(this); enable_tap_to_click_checkbox_->SetMultiLine(true); - enable_vert_edge_scroll_checkbox_ = new views::Checkbox(l10n_util::GetString( - IDS_OPTIONS_SETTINGS_VERT_EDGE_SCROLL_ENABLED_DESCRIPTION)); - enable_vert_edge_scroll_checkbox_->set_listener(this); - enable_vert_edge_scroll_checkbox_->SetMultiLine(true); - // Create speed factor slider with values between 1 and 10 step 1 - speed_factor_slider_ = new views::Slider(1, 10, 1, - static_cast<views::Slider::StyleFlags>( - views::Slider::STYLE_DRAW_VALUE | - views::Slider::STYLE_UPDATE_ON_RELEASE), - this); - // Create sensitivity slider with values between 1 and 10 step 1 - sensitivity_slider_ = new views::Slider(1, 10, 1, + // Create sensitivity slider with values between 1 and 5 step 1 + sensitivity_slider_ = new views::Slider(1, 5, 1, static_cast<views::Slider::StyleFlags>( views::Slider::STYLE_DRAW_VALUE | views::Slider::STYLE_UPDATE_ON_RELEASE), @@ -302,42 +281,22 @@ void TouchpadSection::InitContents(GridLayout* layout) { l10n_util::GetString(IDS_OPTIONS_SETTINGS_SENSITIVITY_DESCRIPTION))); layout->AddView(sensitivity_slider_); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - layout->StartRow(0, double_column_view_set_id()); - layout->AddView(new views::Label( - l10n_util::GetString(IDS_OPTIONS_SETTINGS_SPEED_FACTOR_DESCRIPTION))); - layout->AddView(speed_factor_slider_); - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, single_column_view_set_id()); layout->AddView(enable_tap_to_click_checkbox_); - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - layout->StartRow(0, single_column_view_set_id()); - layout->AddView(enable_vert_edge_scroll_checkbox_); layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); // Init member prefs so we can update the controls if prefs change. tap_to_click_enabled_.Init(prefs::kTapToClickEnabled, profile()->GetPrefs(), this); - vert_edge_scroll_enabled_.Init(prefs::kVertEdgeScrollEnabled, - profile()->GetPrefs(), this); - speed_factor_.Init(prefs::kTouchpadSpeedFactor, - profile()->GetPrefs(), this); sensitivity_.Init(prefs::kTouchpadSensitivity, profile()->GetPrefs(), this); } -void TouchpadSection::NotifyPrefChanged(const std::wstring* pref_name) { +void TouchpadSection::NotifyPrefChanged(const std::string* pref_name) { if (!pref_name || *pref_name == prefs::kTapToClickEnabled) { bool enabled = tap_to_click_enabled_.GetValue(); enable_tap_to_click_checkbox_->SetChecked(enabled); } - if (!pref_name || *pref_name == prefs::kVertEdgeScrollEnabled) { - bool enabled = vert_edge_scroll_enabled_.GetValue(); - enable_vert_edge_scroll_checkbox_->SetChecked(enabled); - } - if (!pref_name || *pref_name == prefs::kTouchpadSpeedFactor) { - double value = speed_factor_.GetValue(); - speed_factor_slider_->SetValue(value); - } if (!pref_name || *pref_name == prefs::kTouchpadSensitivity) { double value = sensitivity_.GetValue(); sensitivity_slider_->SetValue(value); @@ -349,7 +308,8 @@ void TouchpadSection::NotifyPrefChanged(const std::wstring* pref_name) { // TextInput section for text input settings. class LanguageSection : public SettingsPageSection, - public views::ButtonListener { + public views::ButtonListener, + public views::Combobox::Listener { public: explicit LanguageSection(Profile* profile); virtual ~LanguageSection() {} @@ -360,28 +320,58 @@ class LanguageSection : public SettingsPageSection, }; // Overridden from SettingsPageSection: virtual void InitContents(GridLayout* layout); + void NotifyPrefChanged(const std::string* pref_name); // Overridden from views::ButtonListener: virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // Overridden from views::Combobox::Listener: + virtual void ItemChanged(views::Combobox* sender, + int prev_index, + int new_index); + + IntegerPrefMember xkb_remap_search_key_pref_; + IntegerPrefMember xkb_remap_control_key_pref_; + IntegerPrefMember xkb_remap_alt_key_pref_; + views::Combobox* xkb_modifier_combobox_; + chromeos::LanguageComboboxModel<int> xkb_modifier_combobox_model_; + DISALLOW_COPY_AND_ASSIGN(LanguageSection); }; LanguageSection::LanguageSection(Profile* profile) : SettingsPageSection(profile, - IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE) { + IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE), + xkb_modifier_combobox_(NULL), + xkb_modifier_combobox_model_( + &language_prefs::kXkbModifierMultipleChoicePrefs) { + xkb_remap_search_key_pref_.Init( + prefs::kLanguageXkbRemapSearchKeyTo, profile->GetPrefs(), this); + xkb_remap_control_key_pref_.Init( + prefs::kLanguageXkbRemapControlKeyTo, profile->GetPrefs(), this); + xkb_remap_alt_key_pref_.Init( + prefs::kLanguageXkbRemapAltKeyTo, profile->GetPrefs(), this); } void LanguageSection::InitContents(GridLayout* layout) { - // Add the customize button. - layout->StartRow(0, single_column_view_set_id()); + // Add the customize button and XKB combobox. + layout->StartRow(0, double_column_view_set_id()); views::NativeButton* customize_languages_button = new views::NativeButton( this, l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_CUSTOMIZE)); customize_languages_button->set_tag(kCustomizeLanguagesButton); + + xkb_modifier_combobox_ = new views::Combobox(&xkb_modifier_combobox_model_); + xkb_modifier_combobox_->set_listener(this); + + // Initialize the combobox to what's saved in user preferences. Otherwise, + // ItemChanged() will be called with |new_index| == 0. + NotifyPrefChanged(NULL); + layout->AddView(customize_languages_button, 1, 1, GridLayout::LEADING, GridLayout::CENTER); + layout->AddView(xkb_modifier_combobox_); layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); } @@ -392,6 +382,59 @@ void LanguageSection::ButtonPressed( } } +void LanguageSection::ItemChanged(views::Combobox* sender, + int prev_index, + int new_index) { + LOG(INFO) << "Changing XKB modofier pref to " << new_index; + switch (new_index) { + default: + LOG(ERROR) << "Unexpected mapping: " << new_index; + /* fall through */ + case language_prefs::kNoRemap: + xkb_remap_search_key_pref_.SetValue(kSearchKey); + xkb_remap_control_key_pref_.SetValue(kLeftControlKey); + xkb_remap_alt_key_pref_.SetValue(kLeftAltKey); + break; + case language_prefs::kSwapCtrlAndAlt: + xkb_remap_search_key_pref_.SetValue(kSearchKey); + xkb_remap_control_key_pref_.SetValue(kLeftAltKey); + xkb_remap_alt_key_pref_.SetValue(kLeftControlKey); + break; + case language_prefs::kSwapSearchAndCtrl: + xkb_remap_search_key_pref_.SetValue(kLeftControlKey); + xkb_remap_control_key_pref_.SetValue(kSearchKey); + xkb_remap_alt_key_pref_.SetValue(kLeftAltKey); + break; + } +} + +void LanguageSection::NotifyPrefChanged(const std::string* pref_name) { + if (!pref_name || (*pref_name == prefs::kLanguageXkbRemapSearchKeyTo || + *pref_name == prefs::kLanguageXkbRemapControlKeyTo || + *pref_name == prefs::kLanguageXkbRemapAltKeyTo)) { + const int search_remap = xkb_remap_search_key_pref_.GetValue(); + const int control_remap = xkb_remap_control_key_pref_.GetValue(); + const int alt_remap = xkb_remap_alt_key_pref_.GetValue(); + if ((search_remap == kSearchKey) && + (control_remap == kLeftControlKey) && + (alt_remap == kLeftAltKey)) { + xkb_modifier_combobox_->SetSelectedItem(language_prefs::kNoRemap); + } else if ((search_remap == kLeftControlKey) && + (control_remap == kSearchKey) && + (alt_remap == kLeftAltKey)) { + xkb_modifier_combobox_->SetSelectedItem( + language_prefs::kSwapSearchAndCtrl); + } else if ((search_remap == kSearchKey) && + (control_remap == kLeftAltKey) && + (alt_remap == kLeftControlKey)) { + xkb_modifier_combobox_->SetSelectedItem(language_prefs::kSwapCtrlAndAlt); + } else { + LOG(ERROR) << "Unexpected mapping. The prefs are updated by DOMUI?"; + xkb_modifier_combobox_->SetSelectedItem(language_prefs::kNoRemap); + } + } +} + /////////////////////////////////////////////////////////////////////////////// // AccessibilitySection @@ -409,7 +452,7 @@ class AccessibilitySection : public SettingsPageSection, // Overridden from SettingsPageSection: virtual void InitContents(GridLayout* layout); - virtual void NotifyPrefChanged(const std::wstring* pref_name); + virtual void NotifyPrefChanged(const std::string* pref_name); private: // The View that contains the contents of the section. @@ -454,7 +497,7 @@ void AccessibilitySection::ButtonPressed( } } -void AccessibilitySection::NotifyPrefChanged(const std::wstring* pref_name) { +void AccessibilitySection::NotifyPrefChanged(const std::string* pref_name) { if (!pref_name || *pref_name == prefs::kAccessibilityEnabled) { bool enabled = accessibility_enabled_.GetValue(); accessibility_checkbox_->SetChecked(enabled); diff --git a/chrome/browser/chromeos/options/system_page_view.h b/chrome/browser/chromeos/options/system_page_view.h index c560a61..933ff98 100644 --- a/chrome/browser/chromeos/options/system_page_view.h +++ b/chrome/browser/chromeos/options/system_page_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_SYSTEM_PAGE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_SYSTEM_PAGE_VIEW_H_ +#pragma once #include "chrome/browser/chromeos/options/settings_page_view.h" diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc index 6486672..4df5d39 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.cc +++ b/chrome/browser/chromeos/options/wifi_config_view.cc @@ -87,6 +87,16 @@ void WifiConfigView::ContentsChanged(views::Textfield* sender, UpdateCanLogin(); } +bool WifiConfigView::HandleKeystroke( + views::Textfield* sender, + const views::Textfield::Keystroke& keystroke) { + if (sender == passphrase_textfield_ && + keystroke.GetKeyboardCode() == app::VKEY_RETURN) { + parent_->GetDialogClientView()->AcceptWindow(); + } + return false; +} + void WifiConfigView::ButtonPressed(views::Button* sender, const views::Event& event) { if (sender == passphrase_visible_button_) { @@ -124,7 +134,7 @@ bool WifiConfigView::Login() { CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( GetSSID(), GetPassphrase(), identity_string, certificate_path_, - autoconnect_checkbox_->checked()); + autoconnect_checkbox_ ? autoconnect_checkbox_->checked() : true); } else { Save(); CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork( @@ -139,10 +149,12 @@ bool WifiConfigView::Save() { if (!other_network_) { bool changed = false; - bool auto_connect = autoconnect_checkbox_->checked(); - if (auto_connect != wifi_.auto_connect()) { - wifi_.set_auto_connect(auto_connect); - changed = true; + if (autoconnect_checkbox_) { + bool auto_connect = autoconnect_checkbox_->checked(); + if (auto_connect != wifi_.auto_connect()) { + wifi_.set_auto_connect(auto_connect); + changed = true; + } } if (passphrase_textfield_) { @@ -303,15 +315,34 @@ void WifiConfigView::Init() { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); } + // If there's an error, add an error message label. + // Right now, only displaying bad_passphrase and bad_wepkey errors. + if (wifi_.error() == ERROR_BAD_PASSPHRASE || + wifi_.error() == ERROR_BAD_WEPKEY) { + layout->StartRow(0, column_view_set_id); + layout->SkipColumns(1); + int id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_PASSPHRASE; + if (wifi_.error() == ERROR_BAD_WEPKEY) + id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_WEPKEY; + views::Label* label_error = new views::Label(l10n_util::GetString(id)); + label_error->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + label_error->SetColor(SK_ColorRED); + layout->AddView(label_error); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + } + // Autoconnect checkbox - autoconnect_checkbox_ = new views::Checkbox( - l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); - // For other network, default to autoconnect. - bool autoconnect = other_network_ || wifi_.auto_connect(); - autoconnect_checkbox_->SetChecked(autoconnect); - layout->StartRow(0, column_view_set_id); - layout->AddView(autoconnect_checkbox_, 3, 1); - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + // Only show if this network is already remembered (a favorite). + if (wifi_.favorite()) { + autoconnect_checkbox_ = new views::Checkbox(l10n_util::GetString( + IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT)); + // For other network, default to autoconnect. + bool autoconnect = other_network_ || wifi_.auto_connect(); + autoconnect_checkbox_->SetChecked(autoconnect); + layout->StartRow(0, column_view_set_id); + layout->AddView(autoconnect_checkbox_, 3, 1); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + } } } // namespace chromeos diff --git a/chrome/browser/chromeos/options/wifi_config_view.h b/chrome/browser/chromeos/options/wifi_config_view.h index 18c0f78..43078b7 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.h +++ b/chrome/browser/chromeos/options/wifi_config_view.h @@ -1,13 +1,13 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_OPTIONS_WIFI_CONFIG_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_OPTIONS_WIFI_CONFIG_VIEW_H_ +#pragma once #include <string> -#include "base/file_path.h" #include "base/gtest_prod_util.h" #include "base/string16.h" #include "chrome/browser/chromeos/cros/network_library.h" @@ -19,6 +19,8 @@ #include "views/controls/textfield/textfield.h" #include "views/view.h" +class FilePath; + namespace chromeos { class NetworkConfigView; @@ -37,9 +39,7 @@ class WifiConfigView : public views::View, virtual void ContentsChanged(views::Textfield* sender, const string16& new_contents); virtual bool HandleKeystroke(views::Textfield* sender, - const views::Textfield::Keystroke& keystroke) { - return false; - } + const views::Textfield::Keystroke& keystroke); // views::ButtonListener virtual void ButtonPressed(views::Button* sender, const views::Event& event); diff --git a/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc b/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc index 07e846e..4c26499 100644 --- a/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc +++ b/chrome/browser/chromeos/options/wifi_config_view_browsertest.cc @@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/options/wifi_config_view.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" #include "chrome/browser/chromeos/cros/mock_network_library.h" @@ -16,11 +18,14 @@ using ::testing::_; class WifiConfigViewTest : public CrosInProcessBrowserTest { protected: + MockNetworkLibrary *mock_network_library_; + WifiConfigViewTest() : CrosInProcessBrowserTest() {} virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - SetStatusAreaMocksExpectations(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->SetStatusAreaMocksExpectations(); + mock_network_library_ = cros_mock_->mock_network_library(); } }; @@ -34,7 +39,10 @@ IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, NoChangeSaveTest) { // Test that if autoconnect was changed, we call SaveWifiNetwork. IN_PROC_BROWSER_TEST_F(WifiConfigViewTest, ChangeAutoConnectSaveTest) { EXPECT_CALL(*mock_network_library_, SaveWifiNetwork(_)).Times(1); - WifiConfigView* view = new WifiConfigView(NULL, WifiNetwork()); + WifiNetwork remembered_network = WifiNetwork(); + remembered_network.set_favorite(true); + WifiConfigView* view = new WifiConfigView(NULL, remembered_network); + ASSERT_TRUE(view->autoconnect_checkbox_ != NULL); view->autoconnect_checkbox_->SetChecked( !view->autoconnect_checkbox_->checked()); view->Save(); diff --git a/chrome/browser/chromeos/panels/panel_scroller.cc b/chrome/browser/chromeos/panels/panel_scroller.cc index daaa3fc..a568341 100644 --- a/chrome/browser/chromeos/panels/panel_scroller.cc +++ b/chrome/browser/chromeos/panels/panel_scroller.cc @@ -8,9 +8,10 @@ #include "base/logging.h" #include "base/stl_util-inl.h" #include "base/string_util.h" -#include "gfx/canvas.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/panels/panel_scroller_container.h" #include "chrome/browser/chromeos/panels/panel_scroller_header.h" +#include "gfx/canvas.h" #include "views/widget/widget_gtk.h" struct PanelScroller::Panel { diff --git a/chrome/browser/chromeos/panels/panel_scroller.h b/chrome/browser/chromeos/panels/panel_scroller.h index 51eedff..cc9befb 100644 --- a/chrome/browser/chromeos/panels/panel_scroller.h +++ b/chrome/browser/chromeos/panels/panel_scroller.h @@ -1,9 +1,10 @@ -// Copyright (c) 2010 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. +// Copyright (c) 2010 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_PANELS_PANEL_SCROLLER_H_ #define CHROME_BROWSER_CHROMEOS_PANELS_PANEL_SCROLLER_H_ +#pragma once #include <vector> diff --git a/chrome/browser/chromeos/panels/panel_scroller_container.h b/chrome/browser/chromeos/panels/panel_scroller_container.h index 5007e95..76cca15 100644 --- a/chrome/browser/chromeos/panels/panel_scroller_container.h +++ b/chrome/browser/chromeos/panels/panel_scroller_container.h @@ -1,9 +1,10 @@ -// Copyright (c) 2009 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. +// Copyright (c) 2010 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_PANELS_PANEL_SCROLLER_CONTAINER_H_ #define CHROME_BROWSER_CHROMEOS_PANELS_PANEL_SCROLLER_CONTAINER_H_ +#pragma once #include "base/basictypes.h" #include "views/view.h" @@ -34,4 +35,3 @@ class PanelScrollerContainer : public views::View { }; #endif // CHROME_BROWSER_CHROMEOS_PANELS_PANEL_SCROLLER_CONTAINER_H_ - diff --git a/chrome/browser/chromeos/panels/panel_scroller_header.h b/chrome/browser/chromeos/panels/panel_scroller_header.h index 701acb5..fe4d7d4 100644 --- a/chrome/browser/chromeos/panels/panel_scroller_header.h +++ b/chrome/browser/chromeos/panels/panel_scroller_header.h @@ -1,11 +1,10 @@ -// Copyright (c) 2009 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. +// Copyright (c) 2010 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_PANELS_PANEL_SCROLLER_HEADER_H_ #define CHROME_BROWSER_CHROMEOS_PANELS_PANEL_SCROLLER_HEADER_H_ - -#include <string> +#pragma once #include "base/basictypes.h" #include "base/string16.h" @@ -18,9 +17,7 @@ class PanelScrollerHeader : public views::View { explicit PanelScrollerHeader(PanelScroller* scroller); virtual ~PanelScrollerHeader(); - void set_title(const string16& title) { - title_ = title; - } + void set_title(const string16& title) { title_ = title; } // views::View overrides. virtual bool OnMousePressed(const views::MouseEvent& event); @@ -39,4 +36,3 @@ class PanelScrollerHeader : public views::View { }; #endif // CHROME_BROWSER_CHROMEOS_PANELS_PANEL_SCROLLER_HEADER_H_ - diff --git a/chrome/browser/chromeos/pipe_reader.cc b/chrome/browser/chromeos/pipe_reader.cc index 2ed5e7c..8664e35 100644 --- a/chrome/browser/chromeos/pipe_reader.cc +++ b/chrome/browser/chromeos/pipe_reader.cc @@ -1,13 +1,28 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/pipe_reader.h" +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "base/file_path.h" #include "base/scoped_ptr.h" namespace chromeos { +PipeReader::PipeReader(const FilePath& pipe_name) + : pipe_(NULL), + pipe_name_(pipe_name.value()) { +} + +PipeReader::~PipeReader() { + if (pipe_) + fclose(pipe_); +} + std::string PipeReader::Read(const uint32 bytes_to_read) { scoped_array<char> buffer(new char[bytes_to_read]); if (pipe_ || (pipe_ = fopen(pipe_name_.c_str(), "r"))) { diff --git a/chrome/browser/chromeos/pipe_reader.h b/chrome/browser/chromeos/pipe_reader.h index d89405d..821fe95 100644 --- a/chrome/browser/chromeos/pipe_reader.h +++ b/chrome/browser/chromeos/pipe_reader.h @@ -1,20 +1,19 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_PIPE_READER_H_ #define CHROME_BROWSER_CHROMEOS_PIPE_READER_H_ +#pragma once -#include <fcntl.h> #include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> #include <unistd.h> #include <string> #include "base/basictypes.h" -#include "base/file_path.h" + +class FilePath; namespace chromeos { @@ -29,14 +28,8 @@ namespace chromeos { class PipeReader { public: - explicit PipeReader(const FilePath& pipe_name) - : pipe_(NULL), - pipe_name_(pipe_name.value()) { - } - virtual ~PipeReader() { - if (pipe_) - fclose(pipe_); - } + explicit PipeReader(const FilePath& pipe_name); + virtual ~PipeReader(); // Reads data from the pipe up until either a '\n' or |bytes_to_read| bytes. virtual std::string Read(const uint32 bytes_to_read); diff --git a/chrome/browser/chromeos/pipe_reader_unittest.cc b/chrome/browser/chromeos/pipe_reader_unittest.cc index e745655..3646b30 100644 --- a/chrome/browser/chromeos/pipe_reader_unittest.cc +++ b/chrome/browser/chromeos/pipe_reader_unittest.cc @@ -1,10 +1,13 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/pipe_reader.h" #include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> #include "base/file_path.h" #include "base/safe_strerror_posix.h" diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc index 7a993d9..6881ae9 100644 --- a/chrome/browser/chromeos/preferences.cc +++ b/chrome/browser/chromeos/preferences.cc @@ -1,18 +1,22 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/preferences.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/input_method_library.h" -#include "chrome/browser/chromeos/cros/synaptics_library.h" +#include "chrome/browser/chromeos/cros/keyboard_library.h" +#include "chrome/browser/chromeos/cros/touchpad_library.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" -#include "chrome/browser/pref_member.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "unicode/timezone.h" @@ -20,76 +24,102 @@ namespace chromeos { static const char kFallbackInputMethodLocale[] = "en-US"; +static const char kTalkAppExtensionId[] = "ggnioahjipcehijkhpdjekioddnjoben"; + +Preferences::Preferences(Profile* profile) + : profile_(profile) { +} // static void Preferences::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kTapToClickEnabled, false); prefs->RegisterBooleanPref(prefs::kLabsMediaplayerEnabled, false); prefs->RegisterBooleanPref(prefs::kLabsAdvancedFilesystemEnabled, false); - prefs->RegisterBooleanPref(prefs::kAccessibilityEnabled, false); - prefs->RegisterBooleanPref(prefs::kVertEdgeScrollEnabled, false); - prefs->RegisterIntegerPref(prefs::kTouchpadSpeedFactor, 9); - prefs->RegisterIntegerPref(prefs::kTouchpadSensitivity, 5); + // Check if the accessibility pref is already registered, which can happen + // in WizardController::RegisterPrefs. We still want to try to register + // the pref here in case of Chrome/Linux with ChromeOS=1. + if (prefs->FindPreference(prefs::kAccessibilityEnabled) == NULL) { + prefs->RegisterBooleanPref(prefs::kAccessibilityEnabled, false); + } + prefs->RegisterIntegerPref(prefs::kLabsTalkEnabled, 0); + prefs->RegisterIntegerPref(prefs::kTouchpadSensitivity, 3); prefs->RegisterStringPref(prefs::kLanguageCurrentInputMethod, ""); prefs->RegisterStringPref(prefs::kLanguagePreviousInputMethod, ""); prefs->RegisterStringPref(prefs::kLanguageHotkeyNextEngineInMenu, - kHotkeyNextEngineInMenu); + language_prefs::kHotkeyNextEngineInMenu); prefs->RegisterStringPref(prefs::kLanguageHotkeyPreviousEngine, - kHotkeyPreviousEngine); - prefs->RegisterStringPref(prefs::kLanguagePreferredLanguages, ""); + language_prefs::kHotkeyPreviousEngine); + prefs->RegisterStringPref(prefs::kLanguagePreferredLanguages, + kFallbackInputMethodLocale); prefs->RegisterStringPref(prefs::kLanguagePreloadEngines, kFallbackInputMethodId); // EN layout - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { - prefs->RegisterBooleanPref(kChewingBooleanPrefs[i].pref_name, - kChewingBooleanPrefs[i].default_pref_value); + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { + prefs->RegisterBooleanPref( + language_prefs::kChewingBooleanPrefs[i].pref_name, + language_prefs::kChewingBooleanPrefs[i].default_pref_value); } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { prefs->RegisterStringPref( - kChewingMultipleChoicePrefs[i].pref_name, - kChewingMultipleChoicePrefs[i].default_pref_value); + language_prefs::kChewingMultipleChoicePrefs[i].pref_name, + language_prefs::kChewingMultipleChoicePrefs[i].default_pref_value); } - prefs->RegisterIntegerPref(kChewingHsuSelKeyType.pref_name, - kChewingHsuSelKeyType.default_pref_value); + prefs->RegisterIntegerPref( + language_prefs::kChewingHsuSelKeyType.pref_name, + language_prefs::kChewingHsuSelKeyType.default_pref_value); - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { - prefs->RegisterIntegerPref(kChewingIntegerPrefs[i].pref_name, - kChewingIntegerPrefs[i].default_pref_value); + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { + prefs->RegisterIntegerPref( + language_prefs::kChewingIntegerPrefs[i].pref_name, + language_prefs::kChewingIntegerPrefs[i].default_pref_value); } prefs->RegisterStringPref( prefs::kLanguageHangulKeyboard, - kHangulKeyboardNameIDPairs[0].keyboard_id); - prefs->RegisterStringPref(prefs::kLanguageHangulHanjaKeys, kHangulHanjaKeys); - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { - prefs->RegisterBooleanPref(kPinyinBooleanPrefs[i].pref_name, - kPinyinBooleanPrefs[i].default_pref_value); - } - for (size_t i = 0; i < kNumPinyinIntegerPrefs; ++i) { - prefs->RegisterIntegerPref(kPinyinIntegerPrefs[i].pref_name, - kPinyinIntegerPrefs[i].default_pref_value); - } - prefs->RegisterIntegerPref(kPinyinDoublePinyinSchema.pref_name, - kPinyinDoublePinyinSchema.default_pref_value); + language_prefs::kHangulKeyboardNameIDPairs[0].keyboard_id); + prefs->RegisterStringPref(prefs::kLanguageHangulHanjaKeys, + language_prefs::kHangulHanjaKeys); + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { + prefs->RegisterBooleanPref( + language_prefs::kPinyinBooleanPrefs[i].pref_name, + language_prefs::kPinyinBooleanPrefs[i].default_pref_value); + } + for (size_t i = 0; i < language_prefs::kNumPinyinIntegerPrefs; ++i) { + prefs->RegisterIntegerPref( + language_prefs::kPinyinIntegerPrefs[i].pref_name, + language_prefs::kPinyinIntegerPrefs[i].default_pref_value); + } + prefs->RegisterIntegerPref( + language_prefs::kPinyinDoublePinyinSchema.pref_name, + language_prefs::kPinyinDoublePinyinSchema.default_pref_value); - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { - prefs->RegisterBooleanPref(kMozcBooleanPrefs[i].pref_name, - kMozcBooleanPrefs[i].default_pref_value); + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { + prefs->RegisterBooleanPref( + language_prefs::kMozcBooleanPrefs[i].pref_name, + language_prefs::kMozcBooleanPrefs[i].default_pref_value); } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { prefs->RegisterStringPref( - kMozcMultipleChoicePrefs[i].pref_name, - kMozcMultipleChoicePrefs[i].default_pref_value); - } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { - prefs->RegisterIntegerPref(kMozcIntegerPrefs[i].pref_name, - kMozcIntegerPrefs[i].default_pref_value); - } + language_prefs::kMozcMultipleChoicePrefs[i].pref_name, + language_prefs::kMozcMultipleChoicePrefs[i].default_pref_value); + } + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { + prefs->RegisterIntegerPref( + language_prefs::kMozcIntegerPrefs[i].pref_name, + language_prefs::kMozcIntegerPrefs[i].default_pref_value); + } + prefs->RegisterIntegerPref(prefs::kLanguageXkbRemapSearchKeyTo, kSearchKey); + prefs->RegisterIntegerPref(prefs::kLanguageXkbRemapControlKeyTo, + kLeftControlKey); + prefs->RegisterIntegerPref(prefs::kLanguageXkbRemapAltKeyTo, kLeftAltKey); + prefs->RegisterBooleanPref(prefs::kLanguageXkbAutoRepeatEnabled, true); + prefs->RegisterIntegerPref(prefs::kLanguageXkbAutoRepeatDelay, + language_prefs::kXkbAutoRepeatDelayInMs); + prefs->RegisterIntegerPref(prefs::kLanguageXkbAutoRepeatInterval, + language_prefs::kXkbAutoRepeatIntervalInMs); } void Preferences::Init(PrefService* prefs) { tap_to_click_enabled_.Init(prefs::kTapToClickEnabled, prefs, this); accessibility_enabled_.Init(prefs::kAccessibilityEnabled, prefs, this); - vert_edge_scroll_enabled_.Init(prefs::kVertEdgeScrollEnabled, prefs, this); - speed_factor_.Init(prefs::kTouchpadSpeedFactor, prefs, this); sensitivity_.Init(prefs::kTouchpadSensitivity, prefs, this); language_hotkey_next_engine_in_menu_.Init( prefs::kLanguageHotkeyNextEngineInMenu, prefs, this); @@ -98,68 +128,59 @@ void Preferences::Init(PrefService* prefs) { language_preferred_languages_.Init(prefs::kLanguagePreferredLanguages, prefs, this); language_preload_engines_.Init(prefs::kLanguagePreloadEngines, prefs, this); - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { language_chewing_boolean_prefs_[i].Init( - kChewingBooleanPrefs[i].pref_name, prefs, this); + language_prefs::kChewingBooleanPrefs[i].pref_name, prefs, this); } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { language_chewing_multiple_choice_prefs_[i].Init( - kChewingMultipleChoicePrefs[i].pref_name, prefs, this); + language_prefs::kChewingMultipleChoicePrefs[i].pref_name, prefs, this); } language_chewing_hsu_sel_key_type_.Init( - kChewingHsuSelKeyType.pref_name, prefs, this); - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { + language_prefs::kChewingHsuSelKeyType.pref_name, prefs, this); + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { language_chewing_integer_prefs_[i].Init( - kChewingIntegerPrefs[i].pref_name, prefs, this); + language_prefs::kChewingIntegerPrefs[i].pref_name, prefs, this); } language_hangul_keyboard_.Init(prefs::kLanguageHangulKeyboard, prefs, this); language_hangul_hanja_keys_.Init( prefs::kLanguageHangulHanjaKeys, prefs, this); - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { language_pinyin_boolean_prefs_[i].Init( - kPinyinBooleanPrefs[i].pref_name, prefs, this); + language_prefs::kPinyinBooleanPrefs[i].pref_name, prefs, this); } - for (size_t i = 0; i < kNumPinyinIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumPinyinIntegerPrefs; ++i) { language_pinyin_int_prefs_[i].Init( - kPinyinIntegerPrefs[i].pref_name, prefs, this); + language_prefs::kPinyinIntegerPrefs[i].pref_name, prefs, this); } language_pinyin_double_pinyin_schema_.Init( - kPinyinDoublePinyinSchema.pref_name, prefs, this); - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { + language_prefs::kPinyinDoublePinyinSchema.pref_name, prefs, this); + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { language_mozc_boolean_prefs_[i].Init( - kMozcBooleanPrefs[i].pref_name, prefs, this); + language_prefs::kMozcBooleanPrefs[i].pref_name, prefs, this); } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { language_mozc_multiple_choice_prefs_[i].Init( - kMozcMultipleChoicePrefs[i].pref_name, prefs, this); + language_prefs::kMozcMultipleChoicePrefs[i].pref_name, prefs, this); } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { language_mozc_integer_prefs_[i].Init( - kMozcIntegerPrefs[i].pref_name, prefs, this); - } + language_prefs::kMozcIntegerPrefs[i].pref_name, prefs, this); + } + language_xkb_remap_search_key_to_.Init( + prefs::kLanguageXkbRemapSearchKeyTo, prefs, this); + language_xkb_remap_control_key_to_.Init( + prefs::kLanguageXkbRemapControlKeyTo, prefs, this); + language_xkb_remap_alt_key_to_.Init( + prefs::kLanguageXkbRemapAltKeyTo, prefs, this); + language_xkb_auto_repeat_enabled_.Init( + prefs::kLanguageXkbAutoRepeatEnabled, prefs, this); + language_xkb_auto_repeat_delay_pref_.Init( + prefs::kLanguageXkbAutoRepeatDelay, prefs, this); + language_xkb_auto_repeat_interval_pref_.Init( + prefs::kLanguageXkbAutoRepeatInterval, prefs, this); - std::string locale(g_browser_process->GetApplicationLocale()); - // Add input methods based on the application locale when the user first - // logs in. For instance, if the user chooses Japanese as the UI - // language at the first login, we'll add input methods associated with - // Japanese, such as mozc. - if (locale != kFallbackInputMethodLocale && - !prefs->HasPrefPath(prefs::kLanguagePreloadEngines)) { - std::string preload_engines(language_preload_engines_.GetValue()); - std::vector<std::string> input_method_ids; - input_method::GetInputMethodIdsFromLanguageCode( - locale, input_method::kAllInputMethods, &input_method_ids); - if (!input_method_ids.empty()) { - if (!preload_engines.empty()) - preload_engines += ','; - preload_engines += JoinString(input_method_ids, ','); - } - language_preload_engines_.SetValue(preload_engines); - } - // Add the UI language to the preferred languages the user first logs in. - if (!prefs->HasPrefPath(prefs::kLanguagePreferredLanguages)) { - language_preferred_languages_.SetValue(locale); - } + labs_talk_enabled_.Init(prefs::kLabsTalkEnabled, prefs, this); // Initialize touchpad settings to what's saved in user preferences. NotifyPrefChanged(NULL); @@ -169,29 +190,17 @@ void Preferences::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::PREF_CHANGED) - NotifyPrefChanged(Details<std::wstring>(details).ptr()); + NotifyPrefChanged(Details<std::string>(details).ptr()); } -void Preferences::NotifyPrefChanged(const std::wstring* pref_name) { +void Preferences::NotifyPrefChanged(const std::string* pref_name) { if (!pref_name || *pref_name == prefs::kTapToClickEnabled) { - CrosLibrary::Get()->GetSynapticsLibrary()->SetBoolParameter( - PARAM_BOOL_TAP_TO_CLICK, + CrosLibrary::Get()->GetTouchpadLibrary()->SetTapToClick( tap_to_click_enabled_.GetValue()); } - if (!pref_name || *pref_name == prefs::kVertEdgeScrollEnabled) { - CrosLibrary::Get()->GetSynapticsLibrary()->SetBoolParameter( - PARAM_BOOL_VERTICAL_EDGE_SCROLLING, - vert_edge_scroll_enabled_.GetValue()); - } - if (!pref_name || *pref_name == prefs::kTouchpadSpeedFactor) { - CrosLibrary::Get()->GetSynapticsLibrary()->SetRangeParameter( - PARAM_RANGE_SPEED_SENSITIVITY, - speed_factor_.GetValue()); - } if (!pref_name || *pref_name == prefs::kTouchpadSensitivity) { - CrosLibrary::Get()->GetSynapticsLibrary()->SetRangeParameter( - PARAM_RANGE_TOUCH_SENSITIVITY, - sensitivity_.GetValue()); + CrosLibrary::Get()->GetTouchpadLibrary()->SetSensitivity( + sensitivity_.GetValue()); } // We don't handle prefs::kLanguageCurrentInputMethod and PreviousInputMethod @@ -199,14 +208,14 @@ void Preferences::NotifyPrefChanged(const std::wstring* pref_name) { if (!pref_name || *pref_name == prefs::kLanguageHotkeyNextEngineInMenu) { SetLanguageConfigStringListAsCSV( - kHotKeySectionName, - kNextEngineInMenuConfigName, + language_prefs::kHotKeySectionName, + language_prefs::kNextEngineInMenuConfigName, language_hotkey_next_engine_in_menu_.GetValue()); } if (!pref_name || *pref_name == prefs::kLanguageHotkeyPreviousEngine) { SetLanguageConfigStringListAsCSV( - kHotKeySectionName, - kPreviousEngineConfigName, + language_prefs::kHotKeySectionName, + language_prefs::kPreviousEngineConfigName, language_hotkey_previous_engine_.GetValue()); } if (!pref_name || *pref_name == prefs::kLanguagePreferredLanguages) { @@ -214,88 +223,126 @@ void Preferences::NotifyPrefChanged(const std::wstring* pref_name) { // preferencs, we don't need to send this to ibus-daemon. } if (!pref_name || *pref_name == prefs::kLanguagePreloadEngines) { - SetLanguageConfigStringListAsCSV(kGeneralSectionName, - kPreloadEnginesConfigName, + SetLanguageConfigStringListAsCSV(language_prefs::kGeneralSectionName, + language_prefs::kPreloadEnginesConfigName, language_preload_engines_.GetValue()); } - for (size_t i = 0; i < kNumChewingBooleanPrefs; ++i) { - if (!pref_name || *pref_name == kChewingBooleanPrefs[i].pref_name) { - SetLanguageConfigBoolean(kChewingSectionName, - kChewingBooleanPrefs[i].ibus_config_name, - language_chewing_boolean_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumChewingBooleanPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kChewingBooleanPrefs[i].pref_name) { + SetLanguageConfigBoolean( + language_prefs::kChewingSectionName, + language_prefs::kChewingBooleanPrefs[i].ibus_config_name, + language_chewing_boolean_prefs_[i].GetValue()); } } - for (size_t i = 0; i < kNumChewingMultipleChoicePrefs; ++i) { - if (!pref_name || *pref_name == kChewingMultipleChoicePrefs[i].pref_name) { + for (size_t i = 0; i < language_prefs::kNumChewingMultipleChoicePrefs; ++i) { + if (!pref_name || + *pref_name == + language_prefs::kChewingMultipleChoicePrefs[i].pref_name) { SetLanguageConfigString( - kChewingSectionName, - kChewingMultipleChoicePrefs[i].ibus_config_name, + language_prefs::kChewingSectionName, + language_prefs::kChewingMultipleChoicePrefs[i].ibus_config_name, language_chewing_multiple_choice_prefs_[i].GetValue()); } } - if (!pref_name || *pref_name == kChewingHsuSelKeyType.pref_name) { + if (!pref_name || + *pref_name == language_prefs::kChewingHsuSelKeyType.pref_name) { SetLanguageConfigInteger( - kChewingSectionName, - kChewingHsuSelKeyType.ibus_config_name, + language_prefs::kChewingSectionName, + language_prefs::kChewingHsuSelKeyType.ibus_config_name, language_chewing_hsu_sel_key_type_.GetValue()); } - for (size_t i = 0; i < kNumChewingIntegerPrefs; ++i) { - if (!pref_name || *pref_name == kChewingIntegerPrefs[i].pref_name) { - SetLanguageConfigInteger(kChewingSectionName, - kChewingIntegerPrefs[i].ibus_config_name, - language_chewing_integer_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumChewingIntegerPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kChewingIntegerPrefs[i].pref_name) { + SetLanguageConfigInteger( + language_prefs::kChewingSectionName, + language_prefs::kChewingIntegerPrefs[i].ibus_config_name, + language_chewing_integer_prefs_[i].GetValue()); } } - if (!pref_name || *pref_name == prefs::kLanguageHangulKeyboard) { - SetLanguageConfigString(kHangulSectionName, kHangulKeyboardConfigName, + if (!pref_name || + *pref_name == prefs::kLanguageHangulKeyboard) { + SetLanguageConfigString(language_prefs::kHangulSectionName, + language_prefs::kHangulKeyboardConfigName, language_hangul_keyboard_.GetValue()); } if (!pref_name || *pref_name == prefs::kLanguageHangulHanjaKeys) { - SetLanguageConfigString(kHangulSectionName, kHangulHanjaKeysConfigName, + SetLanguageConfigString(language_prefs::kHangulSectionName, + language_prefs::kHangulHanjaKeysConfigName, language_hangul_hanja_keys_.GetValue()); } - for (size_t i = 0; i < kNumPinyinBooleanPrefs; ++i) { - if (!pref_name || *pref_name == kPinyinBooleanPrefs[i].pref_name) { - SetLanguageConfigBoolean(kPinyinSectionName, - kPinyinBooleanPrefs[i].ibus_config_name, - language_pinyin_boolean_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumPinyinBooleanPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kPinyinBooleanPrefs[i].pref_name) { + SetLanguageConfigBoolean( + language_prefs::kPinyinSectionName, + language_prefs::kPinyinBooleanPrefs[i].ibus_config_name, + language_pinyin_boolean_prefs_[i].GetValue()); } } - for (size_t i = 0; i < kNumPinyinIntegerPrefs; ++i) { - if (!pref_name || *pref_name == kPinyinIntegerPrefs[i].pref_name) { - SetLanguageConfigInteger(kPinyinSectionName, - kPinyinIntegerPrefs[i].ibus_config_name, - language_pinyin_int_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumPinyinIntegerPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kPinyinIntegerPrefs[i].pref_name) { + SetLanguageConfigInteger( + language_prefs::kPinyinSectionName, + language_prefs::kPinyinIntegerPrefs[i].ibus_config_name, + language_pinyin_int_prefs_[i].GetValue()); } } - if (!pref_name || *pref_name == kPinyinDoublePinyinSchema.pref_name) { + if (!pref_name || + *pref_name == language_prefs::kPinyinDoublePinyinSchema.pref_name) { SetLanguageConfigInteger( - kPinyinSectionName, - kPinyinDoublePinyinSchema.ibus_config_name, + language_prefs::kPinyinSectionName, + language_prefs::kPinyinDoublePinyinSchema.ibus_config_name, language_pinyin_double_pinyin_schema_.GetValue()); } - for (size_t i = 0; i < kNumMozcBooleanPrefs; ++i) { - if (!pref_name || *pref_name == kMozcBooleanPrefs[i].pref_name) { - SetLanguageConfigBoolean(kMozcSectionName, - kMozcBooleanPrefs[i].ibus_config_name, - language_mozc_boolean_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumMozcBooleanPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kMozcBooleanPrefs[i].pref_name) { + SetLanguageConfigBoolean( + language_prefs::kMozcSectionName, + language_prefs::kMozcBooleanPrefs[i].ibus_config_name, + language_mozc_boolean_prefs_[i].GetValue()); } } - for (size_t i = 0; i < kNumMozcMultipleChoicePrefs; ++i) { - if (!pref_name || *pref_name == kMozcMultipleChoicePrefs[i].pref_name) { + for (size_t i = 0; i < language_prefs::kNumMozcMultipleChoicePrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kMozcMultipleChoicePrefs[i].pref_name) { SetLanguageConfigString( - kMozcSectionName, - kMozcMultipleChoicePrefs[i].ibus_config_name, + language_prefs::kMozcSectionName, + language_prefs::kMozcMultipleChoicePrefs[i].ibus_config_name, language_mozc_multiple_choice_prefs_[i].GetValue()); } } - for (size_t i = 0; i < kNumMozcIntegerPrefs; ++i) { - if (!pref_name || *pref_name == kMozcIntegerPrefs[i].pref_name) { - SetLanguageConfigInteger(kMozcSectionName, - kMozcIntegerPrefs[i].ibus_config_name, - language_mozc_integer_prefs_[i].GetValue()); + for (size_t i = 0; i < language_prefs::kNumMozcIntegerPrefs; ++i) { + if (!pref_name || + *pref_name == language_prefs::kMozcIntegerPrefs[i].pref_name) { + SetLanguageConfigInteger( + language_prefs::kMozcSectionName, + language_prefs::kMozcIntegerPrefs[i].ibus_config_name, + language_mozc_integer_prefs_[i].GetValue()); } } + if (!pref_name || (*pref_name == prefs::kLanguageXkbRemapSearchKeyTo || + *pref_name == prefs::kLanguageXkbRemapControlKeyTo || + *pref_name == prefs::kLanguageXkbRemapAltKeyTo)) { + UpdateModifierKeyMapping(); + } + if (!pref_name || *pref_name == prefs::kLanguageXkbAutoRepeatEnabled) { + const bool enabled = language_xkb_auto_repeat_enabled_.GetValue(); + CrosLibrary::Get()->GetKeyboardLibrary()->SetAutoRepeatEnabled(enabled); + } + if (!pref_name || ((*pref_name == prefs::kLanguageXkbAutoRepeatDelay) || + (*pref_name == prefs::kLanguageXkbAutoRepeatInterval))) { + UpdateAutoRepeatRate(); + } + + // Listen for explicit changes as ExtensionsService handles startup case. + if (pref_name && *pref_name == prefs::kLabsTalkEnabled) { + UpdateTalkApp(); + } } void Preferences::SetLanguageConfigBoolean(const char* section, @@ -355,4 +402,48 @@ void Preferences::SetLanguageConfigStringListAsCSV(const char* section, SetLanguageConfigStringList(section, name, split_values); } +void Preferences::UpdateModifierKeyMapping() { + const int search_remap = language_xkb_remap_search_key_to_.GetValue(); + const int control_remap = language_xkb_remap_control_key_to_.GetValue(); + const int alt_remap = language_xkb_remap_alt_key_to_.GetValue(); + if ((search_remap < kNumModifierKeys) && (search_remap >= 0) && + (control_remap < kNumModifierKeys) && (control_remap >= 0) && + (alt_remap < kNumModifierKeys) && (alt_remap >= 0)) { + chromeos::ModifierMap modifier_map; + modifier_map.push_back( + ModifierKeyPair(kSearchKey, ModifierKey(search_remap))); + modifier_map.push_back( + ModifierKeyPair(kLeftControlKey, ModifierKey(control_remap))); + modifier_map.push_back( + ModifierKeyPair(kLeftAltKey, ModifierKey(alt_remap))); + CrosLibrary::Get()->GetKeyboardLibrary()->RemapModifierKeys(modifier_map); + } else { + LOG(ERROR) << "Failed to remap modifier keys. Unexpected value(s): " + << search_remap << ", " << control_remap << ", " << alt_remap; + } +} + +void Preferences::UpdateAutoRepeatRate() { + AutoRepeatRate rate; + rate.initial_delay_in_ms = language_xkb_auto_repeat_delay_pref_.GetValue(); + rate.repeat_interval_in_ms = + language_xkb_auto_repeat_interval_pref_.GetValue(); + DCHECK(rate.initial_delay_in_ms > 0); + DCHECK(rate.repeat_interval_in_ms > 0); + CrosLibrary::Get()->GetKeyboardLibrary()->SetAutoRepeatRate(rate); +} + +void Preferences::UpdateTalkApp() { + if (!profile_->GetExtensionsService()->is_ready()) { + NOTREACHED() << "Extensions service should be ready"; + return; + } + + if (labs_talk_enabled_.GetValue() == 0) { + profile_->GetExtensionsService()->DisableExtension(kTalkAppExtensionId); + } else { + profile_->GetExtensionsService()->EnableExtension(kTalkAppExtensionId); + } +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/preferences.h b/chrome/browser/chromeos/preferences.h index a9a2250..2226a3a 100644 --- a/chrome/browser/chromeos/preferences.h +++ b/chrome/browser/chromeos/preferences.h @@ -4,15 +4,17 @@ #ifndef CHROME_BROWSER_CHROMEOS_PREFERENCES_H_ #define CHROME_BROWSER_CHROMEOS_PREFERENCES_H_ +#pragma once #include <string> #include <vector> #include "chrome/browser/chromeos/language_preferences.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/common/notification_observer.h" class PrefService; +class Profile; namespace chromeos { @@ -22,7 +24,7 @@ namespace chromeos { // When the preferences change, we change the settings to reflect the new value. class Preferences : public NotificationObserver { public: - Preferences() {} + explicit Preferences(Profile* profile); virtual ~Preferences() {} // This method will register the prefs associated with Chrome OS settings. @@ -36,13 +38,12 @@ class Preferences : public NotificationObserver { const NotificationSource& source, const NotificationDetails& details); - protected: + private: // This will set the OS settings when the preference changes. // If this method is called with NULL, it will set all OS settings to what's // stored in the preferences. - virtual void NotifyPrefChanged(const std::wstring* pref_name); + void NotifyPrefChanged(const std::string* pref_name); - private: // Writes boolean |value| to the input method (IBus) configuration daemon. // |section| (e.g. "general") and |name| (e.g. "use_global_engine") should // not be NULL. @@ -74,6 +75,19 @@ class Preferences : public NotificationObserver { const char* name, const std::string& value); + // Updates the mapping of modifier keys following current prefs values. + void UpdateModifierKeyMapping(); + + // Updates the initial key repeat delay and key repeat interval following + // current prefs values. We set the delay and interval at once since an + // underlying XKB API requires it. + void UpdateAutoRepeatRate(); + + // Updates whether the Talk app is enabled. + void UpdateTalkApp(); + + Profile* profile_; + BooleanPrefMember tap_to_click_enabled_; BooleanPrefMember vert_edge_scroll_enabled_; BooleanPrefMember accessibility_enabled_; @@ -85,20 +99,35 @@ class Preferences : public NotificationObserver { StringPrefMember language_hotkey_previous_engine_; StringPrefMember language_preferred_languages_; StringPrefMember language_preload_engines_; - BooleanPrefMember language_chewing_boolean_prefs_[kNumChewingBooleanPrefs]; + BooleanPrefMember language_chewing_boolean_prefs_[ + language_prefs::kNumChewingBooleanPrefs]; StringPrefMember language_chewing_multiple_choice_prefs_[ - kNumChewingMultipleChoicePrefs]; + language_prefs::kNumChewingMultipleChoicePrefs]; IntegerPrefMember language_chewing_hsu_sel_key_type_; - IntegerPrefMember language_chewing_integer_prefs_[kNumChewingIntegerPrefs]; + IntegerPrefMember language_chewing_integer_prefs_[ + language_prefs::kNumChewingIntegerPrefs]; StringPrefMember language_hangul_keyboard_; StringPrefMember language_hangul_hanja_keys_; - BooleanPrefMember language_pinyin_boolean_prefs_[kNumPinyinBooleanPrefs]; - IntegerPrefMember language_pinyin_int_prefs_[kNumPinyinIntegerPrefs]; + BooleanPrefMember language_pinyin_boolean_prefs_[ + language_prefs::kNumPinyinBooleanPrefs]; + IntegerPrefMember language_pinyin_int_prefs_[ + language_prefs::kNumPinyinIntegerPrefs]; IntegerPrefMember language_pinyin_double_pinyin_schema_; - BooleanPrefMember language_mozc_boolean_prefs_[kNumMozcBooleanPrefs]; + BooleanPrefMember language_mozc_boolean_prefs_[ + language_prefs::kNumMozcBooleanPrefs]; StringPrefMember language_mozc_multiple_choice_prefs_[ - kNumMozcMultipleChoicePrefs]; - IntegerPrefMember language_mozc_integer_prefs_[kNumMozcIntegerPrefs]; + language_prefs::kNumMozcMultipleChoicePrefs]; + IntegerPrefMember language_mozc_integer_prefs_[ + language_prefs::kNumMozcIntegerPrefs]; + IntegerPrefMember language_xkb_remap_search_key_to_; + IntegerPrefMember language_xkb_remap_control_key_to_; + IntegerPrefMember language_xkb_remap_alt_key_to_; + BooleanPrefMember language_xkb_auto_repeat_enabled_; + IntegerPrefMember language_xkb_auto_repeat_delay_pref_; + IntegerPrefMember language_xkb_auto_repeat_interval_pref_; + + // Labs preferences. + IntegerPrefMember labs_talk_enabled_; DISALLOW_COPY_AND_ASSIGN(Preferences); }; diff --git a/chrome/browser/chromeos/pulse_audio_mixer.cc b/chrome/browser/chromeos/pulse_audio_mixer.cc index a091c8c..7cc4d3e 100644 --- a/chrome/browser/chromeos/pulse_audio_mixer.cc +++ b/chrome/browser/chromeos/pulse_audio_mixer.cc @@ -21,8 +21,6 @@ namespace chromeos { // synchronously get the value back. // // TODO(davej): Serialize volume/mute to preserve settings when restarting? -// TODO(davej): Check if we need some thread safety mechanism (will someone be -// calling GetVolume while another process is calling SetVolume?) namespace { @@ -30,14 +28,15 @@ const int kInvalidDeviceId = -1; // Used for passing custom data to the PulseAudio callbacks. struct CallbackWrapper { - pa_threaded_mainloop* mainloop; - void* data; + PulseAudioMixer* instance; + bool done; + void* userdata; }; } // namespace // AudioInfo contains all the values we care about when getting info for a -// Sink (output device) used by GetAudioInfo() +// Sink (output device) used by GetAudioInfo(). struct PulseAudioMixer::AudioInfo { pa_cvolume cvolume; bool muted; @@ -46,6 +45,8 @@ struct PulseAudioMixer::AudioInfo { PulseAudioMixer::PulseAudioMixer() : device_id_(kInvalidDeviceId), last_channels_(0), + mainloop_lock_count_(0), + mixer_state_lock_(), mixer_state_(UNINITIALIZED), pa_context_(NULL), pa_mainloop_(NULL), @@ -55,48 +56,48 @@ PulseAudioMixer::PulseAudioMixer() PulseAudioMixer::~PulseAudioMixer() { PulseAudioFree(); thread_->Stop(); + thread_.reset(); } bool PulseAudioMixer::Init(InitDoneCallback* callback) { - // Just start up worker thread, then post the task of starting up, which can - // block for 200-500ms, so best not to do it on this thread. - if (mixer_state_ != UNINITIALIZED) + if (!InitThread()) return false; - mixer_state_ = INITIALIZING; - if (thread_ == NULL) { - thread_.reset(new base::Thread("PulseAudioMixer")); - if (!thread_->Start()) { - thread_.reset(); - return false; - } - } + // Post the task of starting up, which can block for 200-500ms, + // so best not to do it on the caller's thread. thread_->message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(this, &PulseAudioMixer::DoInit, - callback)); + NewRunnableMethod(this, &PulseAudioMixer::DoInit, callback)); return true; } +bool PulseAudioMixer::InitSync() { + if (!InitThread()) + return false; + return PulseAudioInit(); +} + double PulseAudioMixer::GetVolumeDb() const { - if (!PulseAudioValid()) - return pa_sw_volume_to_dB(0); // this returns -inf + if (!MainloopLockIfReady()) + return pa_sw_volume_to_dB(0); // this returns -inf. AudioInfo data; GetAudioInfo(&data); + MainloopUnlock(); return pa_sw_volume_to_dB(data.cvolume.values[0]); } -void PulseAudioMixer::GetVolumeDbAsync(GetVolumeCallback* callback, +bool PulseAudioMixer::GetVolumeDbAsync(GetVolumeCallback* callback, void* user) { - if (!PulseAudioValid()) - return; + if (CheckState() != READY) + return false; thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &PulseAudioMixer::DoGetVolume, callback, user)); + return true; } void PulseAudioMixer::SetVolumeDb(double vol_db) { - if (!PulseAudioValid()) + if (!MainloopLockIfReady()) return; // last_channels_ determines the number of channels on the main output device, @@ -110,40 +111,38 @@ void PulseAudioMixer::SetVolumeDb(double vol_db) { pa_operation* pa_op; pa_cvolume cvolume; pa_cvolume_set(&cvolume, last_channels_, pa_sw_volume_from_dB(vol_db)); - pa_threaded_mainloop_lock(pa_mainloop_); pa_op = pa_context_set_sink_volume_by_index(pa_context_, device_id_, &cvolume, NULL, NULL); pa_operation_unref(pa_op); - pa_threaded_mainloop_unlock(pa_mainloop_); + MainloopUnlock(); } bool PulseAudioMixer::IsMute() const { - if (!PulseAudioValid()) + if (!MainloopLockIfReady()) return false; AudioInfo data; GetAudioInfo(&data); + MainloopUnlock(); return data.muted; } void PulseAudioMixer::SetMute(bool mute) { - if (!PulseAudioValid()) + if (!MainloopLockIfReady()) return; pa_operation* pa_op; - pa_threaded_mainloop_lock(pa_mainloop_); pa_op = pa_context_set_sink_mute_by_index(pa_context_, device_id_, mute ? 1 : 0, NULL, NULL); pa_operation_unref(pa_op); - pa_threaded_mainloop_unlock(pa_mainloop_); + MainloopUnlock(); } -bool PulseAudioMixer::IsValid() const { - if (mixer_state_ == READY) - return true; - if (!pa_context_) - return false; - if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY) - return false; - return true; +PulseAudioMixer::State PulseAudioMixer::CheckState() const { + AutoLock lock(mixer_state_lock_); + // If we think it's ready, verify it is actually so. + if ((mixer_state_ == READY) && + (pa_context_get_state(pa_context_) != PA_CONTEXT_READY)) + mixer_state_ = IN_ERROR; + return mixer_state_; } //////////////////////////////////////////////////////////////////////////////// @@ -161,17 +160,23 @@ void PulseAudioMixer::DoGetVolume(GetVolumeCallback* callback, delete callback; } -struct ConnectToPulseCallbackData { - PulseAudioMixer* instance; - bool connect_done; -}; +bool PulseAudioMixer::InitThread() { + if (thread_ == NULL) { + thread_.reset(new base::Thread("PulseAudioMixer")); + if (!thread_->Start()) { + thread_.reset(); + return false; + } + } + return true; +} // static void PulseAudioMixer::ConnectToPulseCallbackThunk( pa_context* context, void* userdata) { - ConnectToPulseCallbackData* data = - static_cast<ConnectToPulseCallbackData*>(userdata); - data->instance->OnConnectToPulseCallback(context, &data->connect_done); + CallbackWrapper* data = + static_cast<CallbackWrapper*>(userdata); + data->instance->OnConnectToPulseCallback(context, &data->done); } void PulseAudioMixer::OnConnectToPulseCallback( @@ -182,13 +187,20 @@ void PulseAudioMixer::OnConnectToPulseCallback( state == PA_CONTEXT_TERMINATED) { // Connection process has reached a terminal state. Wake PulseAudioInit(). *connect_done = true; - pa_threaded_mainloop_signal(pa_mainloop_, 0); + MainloopSignal(); } } bool PulseAudioMixer::PulseAudioInit() { pa_context_state_t state = PA_CONTEXT_FAILED; + { + AutoLock lock(mixer_state_lock_); + if (mixer_state_ != UNINITIALIZED) + return false; + mixer_state_ = INITIALIZING; + } + while (true) { // Create connection to default server. pa_mainloop_ = pa_threaded_mainloop_new(); @@ -202,7 +214,8 @@ bool PulseAudioMixer::PulseAudioInit() { break; } - pa_threaded_mainloop_lock(pa_mainloop_); + if (!MainloopSafeLock()) + return false; while (true) { pa_mainloop_api* pa_mlapi = pa_threaded_mainloop_get_api(pa_mainloop_); @@ -217,10 +230,11 @@ bool PulseAudioMixer::PulseAudioInit() { break; } - ConnectToPulseCallbackData data; - data.instance = this; - data.connect_done = false; + MainloopUnlock(); + if (!MainloopSafeLock()) + return false; + CallbackWrapper data = {this, false, NULL}; pa_context_set_state_callback(pa_context_, &ConnectToPulseCallbackThunk, &data); @@ -231,13 +245,13 @@ bool PulseAudioMixer::PulseAudioInit() { } else { // Wait until we have a completed connection or fail. do { - pa_threaded_mainloop_wait(pa_mainloop_); - } while (!data.connect_done); + MainloopWait(); + } while (!data.done); state = pa_context_get_state(pa_context_); if (state == PA_CONTEXT_FAILED) { - LOG(ERROR) << "PulseAudio context connection failed"; + LOG(ERROR) << "PulseAudio connection failed (daemon not running?)"; } else if (state == PA_CONTEXT_TERMINATED) { LOG(ERROR) << "PulseAudio connection terminated early"; } else if (state != PA_CONTEXT_READY) { @@ -249,15 +263,20 @@ bool PulseAudioMixer::PulseAudioInit() { break; } - pa_threaded_mainloop_unlock(pa_mainloop_); + MainloopUnlock(); if (state != PA_CONTEXT_READY) break; - last_channels_ = 0; + if (!MainloopSafeLock()) + return false; GetDefaultPlaybackDevice(); - mixer_state_ = READY; + MainloopUnlock(); + if (device_id_ == kInvalidDeviceId) + break; + + set_mixer_state(READY); return true; } @@ -270,78 +289,76 @@ void PulseAudioMixer::PulseAudioFree() { if (!pa_mainloop_) return; - DCHECK_NE(mixer_state_, UNINITIALIZED); - mixer_state_ = SHUTTING_DOWN; + { + AutoLock lock(mixer_state_lock_); + DCHECK_NE(mixer_state_, UNINITIALIZED); + if (mixer_state_ == SHUTTING_DOWN) + return; + // If still initializing on another thread, this will cause it to exit. + mixer_state_ = SHUTTING_DOWN; + } + MainloopLock(); if (pa_context_) { - pa_threaded_mainloop_lock(pa_mainloop_); pa_context_disconnect(pa_context_); pa_context_unref(pa_context_); - pa_threaded_mainloop_unlock(pa_mainloop_); pa_context_ = NULL; } + MainloopUnlock(); pa_threaded_mainloop_stop(pa_mainloop_); pa_threaded_mainloop_free(pa_mainloop_); pa_mainloop_ = NULL; - mixer_state_ = UNINITIALIZED; -} - -bool PulseAudioMixer::PulseAudioValid() const { - if (mixer_state_ != READY) - return false; - if (!pa_context_) { - DLOG(ERROR) << "Trying to use PulseAudio when no context"; - return false; - } - if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY) { - LOG(ERROR) << "PulseAudio context not ready (" - << pa_context_get_state(pa_context_) << ")"; - return false; - } - if (device_id_ == kInvalidDeviceId) - return false; - - return true; + set_mixer_state(UNINITIALIZED); } -void PulseAudioMixer::CompleteOperationAndUnlock(pa_operation* pa_op) const { +void PulseAudioMixer::CompleteOperation(pa_operation* pa_op, + bool* done) const { // After starting any operation, this helper checks if it started OK, then // waits for it to complete by iterating through the mainloop until the // operation is not running anymore. CHECK(pa_op); while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { - pa_threaded_mainloop_wait(pa_mainloop_); + // If operation still running, but we got what we needed, cancel it now. + if (*done) { + pa_operation_cancel(pa_op); + break; + } + MainloopWait(); } pa_operation_unref(pa_op); - pa_threaded_mainloop_unlock(pa_mainloop_); } +// Must be called with mainloop lock held void PulseAudioMixer::GetDefaultPlaybackDevice() { + DCHECK_GT(mainloop_lock_count_, 0); DCHECK(pa_context_); DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY); - pa_threaded_mainloop_lock(pa_mainloop_); + CallbackWrapper data = {this, false, NULL}; + pa_operation* pa_op = pa_context_get_sink_info_list(pa_context_, EnumerateDevicesCallback, - this); - CompleteOperationAndUnlock(pa_op); + &data); + CompleteOperation(pa_op, &data.done); return; } void PulseAudioMixer::OnEnumerateDevices(const pa_sink_info* sink_info, - int eol) { - // If eol is set to a positive number, you're at the end of the list. - if (eol > 0) + int eol, bool* done) { + if (device_id_ != kInvalidDeviceId) return; // TODO(davej): Should we handle cases of more than one output sink device? - if (device_id_ == kInvalidDeviceId) - device_id_ = sink_info->index; - pa_threaded_mainloop_signal(pa_mainloop_, 0); + // eol is < 0 for error, > 0 for end of list, ==0 while listing. + if (eol == 0) { + device_id_ = sink_info->index; + } + *done = true; + MainloopSignal(); } // static @@ -349,19 +366,20 @@ void PulseAudioMixer::EnumerateDevicesCallback(pa_context* unused, const pa_sink_info* sink_info, int eol, void* userdata) { - PulseAudioMixer* inst = static_cast<PulseAudioMixer*>(userdata); - inst->OnEnumerateDevices(sink_info, eol); + CallbackWrapper* data = + static_cast<CallbackWrapper*>(userdata); + data->instance->OnEnumerateDevices(sink_info, eol, &data->done); } +// Must be called with lock held void PulseAudioMixer::GetAudioInfo(AudioInfo* info) const { - CallbackWrapper cb_data = {pa_mainloop_, info}; - pa_threaded_mainloop_lock(pa_mainloop_); - pa_operation* pa_op; - pa_op = pa_context_get_sink_info_by_index(pa_context_, - device_id_, - GetAudioInfoCallback, - &cb_data); - CompleteOperationAndUnlock(pa_op); + DCHECK_GT(mainloop_lock_count_, 0); + CallbackWrapper data = {const_cast<PulseAudioMixer*>(this), false, info}; + pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_, + device_id_, + GetAudioInfoCallback, + &data); + CompleteOperation(pa_op, &data.done); } // static @@ -369,15 +387,58 @@ void PulseAudioMixer::GetAudioInfoCallback(pa_context* unused, const pa_sink_info* sink_info, int eol, void* userdata) { - CallbackWrapper* cb_data = static_cast<CallbackWrapper*>(userdata); - AudioInfo* data = static_cast<AudioInfo*>(cb_data->data); + CallbackWrapper* data = static_cast<CallbackWrapper*>(userdata); + AudioInfo* info = static_cast<AudioInfo*>(data->userdata); // Copy just the information we care about. if (eol == 0) { - data->cvolume = sink_info->volume; - data->muted = sink_info->mute ? true : false; + info->cvolume = sink_info->volume; + info->muted = sink_info->mute ? true : false; + data->done = true; } - pa_threaded_mainloop_signal(cb_data->mainloop, 0); + data->instance->MainloopSignal(); +} + +inline void PulseAudioMixer::MainloopLock() const { + pa_threaded_mainloop_lock(pa_mainloop_); + ++mainloop_lock_count_; +} + +inline void PulseAudioMixer::MainloopUnlock() const { + --mainloop_lock_count_; + pa_threaded_mainloop_unlock(pa_mainloop_); +} + +// Must be called with the lock held. +inline void PulseAudioMixer::MainloopWait() const { + DCHECK_GT(mainloop_lock_count_, 0); + pa_threaded_mainloop_wait(pa_mainloop_); +} + +// Must be called with the lock held. +inline void PulseAudioMixer::MainloopSignal() const { + DCHECK_GT(mainloop_lock_count_, 0); + pa_threaded_mainloop_signal(pa_mainloop_, 0); +} + +inline bool PulseAudioMixer::MainloopSafeLock() const { + AutoLock lock(mixer_state_lock_); + if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_)) + return false; + pa_threaded_mainloop_lock(pa_mainloop_); + ++mainloop_lock_count_; + return true; +} + +inline bool PulseAudioMixer::MainloopLockIfReady() const { + AutoLock lock(mixer_state_lock_); + if (mixer_state_ != READY) + return false; + if (!pa_mainloop_) + return false; + pa_threaded_mainloop_lock(pa_mainloop_); + ++mainloop_lock_count_; + return true; } } // namespace chromeos diff --git a/chrome/browser/chromeos/pulse_audio_mixer.h b/chrome/browser/chromeos/pulse_audio_mixer.h index 223fe33..5d7de39 100644 --- a/chrome/browser/chromeos/pulse_audio_mixer.h +++ b/chrome/browser/chromeos/pulse_audio_mixer.h @@ -4,9 +4,11 @@ #ifndef CHROME_BROWSER_CHROMEOS_PULSE_AUDIO_MIXER_H_ #define CHROME_BROWSER_CHROMEOS_PULSE_AUDIO_MIXER_H_ +#pragma once #include "base/basictypes.h" #include "base/callback.h" +#include "base/lock.h" #include "base/scoped_ptr.h" #include "base/thread.h" @@ -20,6 +22,14 @@ namespace chromeos { class PulseAudioMixer { public: + enum State { + UNINITIALIZED = 0, + INITIALIZING, + READY, + SHUTTING_DOWN, + IN_ERROR + }; + PulseAudioMixer(); ~PulseAudioMixer(); @@ -28,12 +38,16 @@ class PulseAudioMixer { typedef Callback1<bool>::Type InitDoneCallback; bool Init(InitDoneCallback* callback); + // Blocking init call guarantees PulseAudio is started before returning. + bool InitSync(); + // Blocking call. Returns a default of -inf on error. double GetVolumeDb() const; - // Non-blocking, volume sent in as first param to callback + // Non-blocking, volume sent in as first param to callback. The callback is + // only called if the function returns true. typedef Callback2<double, void*>::Type GetVolumeCallback; - void GetVolumeDbAsync(GetVolumeCallback* callback, void* user); + bool GetVolumeDbAsync(GetVolumeCallback* callback, void* user); // Non-blocking call. void SetVolumeDb(double vol_db); @@ -45,20 +59,14 @@ class PulseAudioMixer { // Non-Blocking call. void SetMute(bool mute); - // Call any time to see if we have a valid working connection to PulseAudio. - // Non-blocking call. - bool IsValid() const; + // Returns READY if we have a valid working connection to PulseAudio. + // This can return IN_ERROR if we lose the connection, even after an original + // successful init. Non-blocking call. + State CheckState() const; private: struct AudioInfo; - enum State { - UNINITIALIZED = 0, - INITIALIZING, - READY, - SHUTTING_DOWN - }; - // These are the tasks to be run in the background on the worker thread. void DoInit(InitDoneCallback* callback); void DoGetVolume(GetVolumeCallback* callback, void* user); @@ -66,6 +74,9 @@ class PulseAudioMixer { static void ConnectToPulseCallbackThunk(pa_context* c, void* userdata); void OnConnectToPulseCallback(pa_context* c, bool* connect_done); + // Helper function to just get our messsage loop thread going. + bool InitThread(); + // This goes through sequence of connecting to the default PulseAudio server. // We will block until we either have a valid connection or something failed. // If a connection is lost for some reason, delete and recreate the object. @@ -74,14 +85,9 @@ class PulseAudioMixer { // PulseAudioFree. Disconnect from server. void PulseAudioFree(); - // Check if the PA system is ready for communication, as well as if a default - // device is available to talk to. This can return false if we lose the - // connection, even after an original successful init. - bool PulseAudioValid() const; - // Iterates the PA mainloop and only returns once an operation has completed - // (successfully or unsuccessfully). This call only blocks the worker thread. - void CompleteOperationAndUnlock(pa_operation* pa_op) const; + // (successfully or unsuccessfully) or *done is true. + void CompleteOperation(pa_operation* pa_op, bool* done) const; // For now, this just gets the first device returned from the enumeration // request. This will be the 'default' or 'master' device that all further @@ -91,7 +97,7 @@ class PulseAudioMixer { const pa_sink_info* sink_info, int eol, void* userdata); - void OnEnumerateDevices(const pa_sink_info* sink_info, int eol); + void OnEnumerateDevices(const pa_sink_info* sink_info, int eol, bool* done); // Get the info we're interested in from the default device. Currently this // is an array of volumes, and the mute state. Blocking call. @@ -101,12 +107,34 @@ class PulseAudioMixer { int eol, void* userdata); + void set_mixer_state(State state) { + AutoLock lock(mixer_state_lock_); + mixer_state_ = state; + } + + // These call down to PulseAudio's mainloop locking functions + void MainloopLock() const; + void MainloopUnlock() const; + void MainloopWait() const; + void MainloopSignal() const; + + // Same as Lock(), but we fail if we are shutting down or mainloop invalid. + bool MainloopSafeLock() const; + + // Lock the mainloop pa_lock_ if mixer_state_ is READY. + bool MainloopLockIfReady() const; + // The PulseAudio index of the main device being used. - mutable int device_id_; + int device_id_; // Set to the number of channels on the main device. int last_channels_; - State mixer_state_; + + // For informational purposes only, just used to assert lock is held. + mutable int mainloop_lock_count_; + + mutable Lock mixer_state_lock_; + mutable State mixer_state_; // Cached contexts for use in PulseAudio calls. pa_context* pa_context_; diff --git a/chrome/browser/chromeos/status/clock_menu_button.cc b/chrome/browser/chromeos/status/clock_menu_button.cc index 9576490..f63593f 100644 --- a/chrome/browser/chromeos/status/clock_menu_button.cc +++ b/chrome/browser/chromeos/status/clock_menu_button.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -9,6 +9,7 @@ #include "base/i18n/time_formatting.h" #include "base/string_util.h" #include "base/time.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/status/status_area_host.h" @@ -25,17 +26,18 @@ namespace chromeos { const int kTimerSlopSeconds = 1; ClockMenuButton::ClockMenuButton(StatusAreaHost* host) - : MenuButton(NULL, std::wstring(), this, false), + : StatusAreaButton(this), host_(host) { // Add as SystemLibrary observer. We update the clock if timezone changes. CrosLibrary::Get()->GetSystemLibrary()->AddObserver(this); set_border(NULL); + set_use_menu_button_paint(true); SetFont(ResourceBundle::GetSharedInstance().GetFont( ResourceBundle::BaseFont).DeriveFont(1, gfx::Font::BOLD)); SetEnabledColor(0xB3FFFFFF); // White with 70% Alpha - SetShowHighlighted(false); - set_alignment(TextButton::ALIGN_RIGHT); + SetShowMultipleIconStates(false); + set_alignment(TextButton::ALIGN_CENTER); UpdateTextAndSetNextTimer(); } @@ -68,18 +70,9 @@ void ClockMenuButton::UpdateTextAndSetNextTimer() { } void ClockMenuButton::UpdateText() { - int cur_width = GetPreferredSize().width(); - SetText(base::TimeFormatTimeOfDay(base::Time::Now())); - // TextButtons normally remember the max text size, so the button's preferred - // size will always be as large as the largest text ever put in it. - // We clear that max text size, so we can adjust the size to fit the text. - ClearMaxTextSize(); - int new_width = GetPreferredSize().width(); - - // If width has changed, we want to relayout the StatusAreaView. - if (new_width != cur_width) - PreferredSizeChanged(); - + base::Time time(base::Time::Now()); + SetText(base::TimeFormatTimeOfDay(time)); + SetTooltipText(base::TimeFormatShortDate(time)); SchedulePaint(); } diff --git a/chrome/browser/chromeos/status/clock_menu_button.h b/chrome/browser/chromeos/status/clock_menu_button.h index b5daa2c..405d00a 100644 --- a/chrome/browser/chromeos/status/clock_menu_button.h +++ b/chrome/browser/chromeos/status/clock_menu_button.h @@ -4,10 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_CLOCK_MENU_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_CLOCK_MENU_BUTTON_H_ +#pragma once #include "base/scoped_ptr.h" #include "base/timer.h" #include "chrome/browser/chromeos/cros/system_library.h" +#include "chrome/browser/chromeos/status/status_area_button.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_service.h" #include "unicode/calendar.h" @@ -21,7 +23,7 @@ class StatusAreaHost; // The clock menu button in the status area. // This button shows the current time. -class ClockMenuButton : public views::MenuButton, +class ClockMenuButton : public StatusAreaButton, public views::ViewMenuDelegate, public menus::MenuModel, public SystemLibrary::Observer { diff --git a/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc b/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc index b0a3106..f7a6f1c 100644 --- a/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc +++ b/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc @@ -12,7 +12,7 @@ #include "chrome/browser/chromeos/frame/browser_view.h" #include "chrome/browser/chromeos/status/status_area_view.h" #include "chrome/browser/chromeos/view_ids.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" #include "chrome/test/in_process_browser_test.h" @@ -25,6 +25,11 @@ namespace chromeos { class ClockMenuButtonTest : public InProcessBrowserTest { protected: ClockMenuButtonTest() : InProcessBrowserTest() {} + virtual void SetUpInProcessBrowserTestFixture() { + // This test requires actual libcros, but InProcessBrowserTest has set + // to use stub, so reset it here. + CrosLibrary::Get()->GetTestApi()->ResetUseStubImpl(); + } ClockMenuButton* GetClockMenuButton() { BrowserView* view = static_cast<BrowserView*>(browser()->window()); return static_cast<StatusAreaView*>(view-> @@ -35,13 +40,18 @@ class ClockMenuButtonTest : public InProcessBrowserTest { IN_PROC_BROWSER_TEST_F(ClockMenuButtonTest, TimezoneTest) { ClockMenuButton* clock = GetClockMenuButton(); ASSERT_TRUE(clock != NULL); + // Update timezone and make sure clock text changes. - std::wstring text_before = clock->text(); - scoped_ptr<icu::TimeZone> timezone(icu::TimeZone::createTimeZone( + scoped_ptr<icu::TimeZone> timezone_first(icu::TimeZone::createTimeZone( icu::UnicodeString::fromUTF8("Asia/Hong_Kong"))); - CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone.get()); + CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone_first.get()); + std::wstring text_before = clock->text(); + scoped_ptr<icu::TimeZone> timezone_second(icu::TimeZone::createTimeZone( + icu::UnicodeString::fromUTF8("Pacific/Samoa"))); + CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone_second.get()); std::wstring text_after = clock->text(); EXPECT_NE(text_before, text_after); + } } // namespace chromeos diff --git a/chrome/browser/chromeos/status/feedback_menu_button.cc b/chrome/browser/chromeos/status/feedback_menu_button.cc index 1b8ead6..d3b14d3 100644 --- a/chrome/browser/chromeos/status/feedback_menu_button.cc +++ b/chrome/browser/chromeos/status/feedback_menu_button.cc @@ -6,6 +6,7 @@ #include <string> +#include "app/l10n_util.h" #include "app/resource_bundle.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/chromeos/status/status_area_host.h" @@ -22,6 +23,7 @@ FeedbackMenuButton::FeedbackMenuButton(StatusAreaHost* host) : StatusAreaButton(this), host_(host) { DCHECK(host_); + SetTooltipText(l10n_util::GetString(IDS_STATUSBAR_FEEDBACK_TOOLTIP)); } FeedbackMenuButton::~FeedbackMenuButton() { diff --git a/chrome/browser/chromeos/status/feedback_menu_button.h b/chrome/browser/chromeos/status/feedback_menu_button.h index 1b6b1df..9e1590b 100644 --- a/chrome/browser/chromeos/status/feedback_menu_button.h +++ b/chrome/browser/chromeos/status/feedback_menu_button.h @@ -4,8 +4,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_FEEDBACK_MENU_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_FEEDBACK_MENU_BUTTON_H_ +#pragma once -#include "app/menus/simple_menu_model.h" #include "chrome/browser/chromeos/status/status_area_button.h" #include "views/controls/menu/menu_2.h" #include "views/controls/menu/view_menu_delegate.h" diff --git a/chrome/browser/chromeos/status/language_menu_button.cc b/chrome/browser/chromeos/status/language_menu_button.cc index 169640d..24f2da5 100644 --- a/chrome/browser/chromeos/status/language_menu_button.cc +++ b/chrome/browser/chromeos/status/language_menu_button.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -8,6 +8,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/time.h" #include "base/utf_string_conversions.h" @@ -15,10 +16,12 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/keyboard_library.h" #include "chrome/browser/chromeos/input_method/input_method_util.h" +#include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/status/status_area_host.h" #include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -71,7 +74,6 @@ enum { // input method list to avoid conflict. const int kRadioGroupLanguage = 1 << 16; const int kRadioGroupNone = -1; -const wchar_t kSpacer[] = L"MMM"; // A mapping from an input method id to a text for the language indicator. The // mapping is necessary since some input methods belong to the same language. @@ -91,13 +93,9 @@ const struct { // For traditional Chinese input methods { "chewing", "\xe9\x85\xb7" }, // U+9177 { "m17n:zh:cangjie", "\xe5\x80\x89" }, // U+5009 - // TODO(yusukes): Add m17n:zh:quick if there's a good Hanzi character for it. - - // Handle "m17n:t" input methods here since ICU is not able to handle the - // language code "t". Note: most users use either latn-pre or latn-post - // methods, not both. The same is true for mozc/mozc-jp. - { "m17n:t:latn-pre", "LAT" }, - { "m17n:t:latn-post", "LAT" }, + { "m17n:zh:quick", "\xe9\x80\x9f" }, // U+901F + // For Hangul input method. + { "hangul", "\xed\x95\x9c" }, // U+D55C }; const size_t kMappingFromIdToIndicatorTextLen = ARRAYSIZE_UNSAFE(kMappingFromIdToIndicatorText); @@ -126,7 +124,7 @@ namespace chromeos { // LanguageMenuButton LanguageMenuButton::LanguageMenuButton(StatusAreaHost* host) - : MenuButton(NULL, std::wstring(), this, false), + : StatusAreaButton(this), input_method_descriptors_(CrosLibrary::Get()->GetInputMethodLibrary()-> GetActiveInputMethods()), model_(NULL), @@ -139,15 +137,16 @@ LanguageMenuButton::LanguageMenuButton(StatusAreaHost* host) DCHECK(input_method_descriptors_.get() && !input_method_descriptors_->empty()); set_border(NULL); + set_use_menu_button_paint(true); SetFont(ResourceBundle::GetSharedInstance().GetFont( ResourceBundle::BaseFont).DeriveFont(1, gfx::Font::BOLD)); SetEnabledColor(0xB3FFFFFF); // White with 70% Alpha SetDisabledColor(0x00FFFFFF); // White with 00% Alpha (invisible) - SetShowHighlighted(false); + SetShowMultipleIconStates(false); + set_alignment(TextButton::ALIGN_CENTER); + // Update the model RebuildModel(); - // Grab the real estate. - UpdateIndicator(kSpacer, L"" /* no tooltip */); // Draw the default indicator "US". The default indicator "US" is used when // |pref_service| is not available (for example, unit tests) or |pref_service| @@ -325,12 +324,7 @@ string16 LanguageMenuButton::GetLabelAt(int index) const { std::wstring name; if (IndexIsInInputMethodList(index)) { - const std::string language_code = - input_method::GetLanguageCodeFromDescriptor( - input_method_descriptors_->at(index)); - bool need_method_name = (need_method_name_.count(language_code) > 0); - name = GetTextForMenu(input_method_descriptors_->at(index), - need_method_name); + name = GetTextForMenu(input_method_descriptors_->at(index)); } else if (GetPropertyIndex(index, &index)) { const ImePropertyList& property_list = CrosLibrary::Get()->GetInputMethodLibrary()->current_ime_properties(); @@ -398,6 +392,22 @@ void LanguageMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { GetActiveInputMethods()); RebuildModel(); language_menu_.Rebuild(); + + // Disallow the menu widget to grab the keyboard focus. This is necessary to + // enable users to change status of an input method (e.g. change the input + // mode from Japanese Hiragana to Japanese Katakana) without discarding a + // preedit string. See crosbug.com/5796 for details. Note that menus other + // than this one should not call the Gtk+ API since it is a special API only + // for a menu related to IME/keyboard. See the Gtk+ API reference at: + // http://library.gnome.org/devel/gtk/stable/GtkMenuShell.html + gfx::NativeMenu native_menu = language_menu_.GetNativeMenu(); + if (native_menu) { + gtk_menu_shell_set_take_focus(GTK_MENU_SHELL(native_menu), FALSE); + } else { + LOG(ERROR) + << "Can't call gtk_menu_shell_set_take_focus since NativeMenu is NULL"; + } + language_menu_.UpdateStates(); language_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); } @@ -428,7 +438,7 @@ void LanguageMenuButton::InputMethodChanged(InputMethodLibrary* obj) { // buttun for the login screen is destroyed. if (!logged_in_ && g_browser_process && g_browser_process->local_state()) { g_browser_process->local_state()->SetString( - kPreferredKeyboardLayout, current_input_method.id); + language_prefs::kPreferredKeyboardLayout, current_input_method.id); g_browser_process->local_state()->SavePersistentPrefs(); } } @@ -446,7 +456,8 @@ void LanguageMenuButton::ImePropertiesChanged(InputMethodLibrary* obj) { //////////////////////////////////////////////////////////////////////////////// // views::View implementation: -void LanguageMenuButton::LocaleChanged() { +void LanguageMenuButton::OnLocaleChanged() { + input_method::OnLocaleChanged(); const InputMethodDescriptor& input_method = CrosLibrary::Get()->GetInputMethodLibrary()->current_input_method(); UpdateIndicatorFromInputMethod(input_method); @@ -474,15 +485,13 @@ void LanguageMenuButton::UpdateIndicator( SetTooltipText(tooltip); } SetText(name); - set_alignment(TextButton::ALIGN_RIGHT); SchedulePaint(); } void LanguageMenuButton::UpdateIndicatorFromInputMethod( const InputMethodDescriptor& input_method) { const std::wstring name = GetTextForIndicator(input_method); - const std::wstring tooltip = - GetTextForMenu(input_method, true /* add_method_name */); + const std::wstring tooltip = GetTextForMenu(input_method); UpdateIndicator(name, tooltip); } @@ -492,26 +501,14 @@ void LanguageMenuButton::RebuildModel() { // Indicates if separator's needed before each section. bool need_separator = false; - need_method_name_.clear(); - std::set<std::string> languages_seen; if (!input_method_descriptors_->empty()) { // We "abuse" the command_id and group_id arguments of AddRadioItem method. // A COMMAND_ID_XXX enum value is passed as command_id, and array index of // |input_method_descriptors_| or |property_list| is passed as group_id. for (size_t i = 0; i < input_method_descriptors_->size(); ++i) { model_->AddRadioItem(COMMAND_ID_INPUT_METHODS, dummy_label, i); - - const std::string language_code - = input_method::GetLanguageCodeFromDescriptor( - input_method_descriptors_->at(i)); - // If there is more than one input method for this language, then we need - // to display the method name. - if (languages_seen.find(language_code) == languages_seen.end()) { - languages_seen.insert(language_code); - } else { - need_method_name_.insert(language_code); - } } + need_separator = true; } @@ -634,31 +631,33 @@ std::wstring LanguageMenuButton::GetTextForIndicator( } std::wstring LanguageMenuButton::GetTextForMenu( - const InputMethodDescriptor& input_method, bool add_method_name) { + const InputMethodDescriptor& input_method) { + // We don't show language here. Name of keyboard layout or input method + // usually imply (or explicitly include) its language. + + // Special case for Dutch, French and German: these languages have multiple + // keyboard layouts and share the same laout of keyboard (Belgian). We need to + // show explicitly the language for the layout. + // For Arabic and Hindi: they share "Standard Input Method". const std::string language_code = input_method::GetLanguageCodeFromDescriptor(input_method); - std::wstring text; - if (language_code == "t") { - text = UTF8ToWide(input_method.display_name); + if (language_code == "ar" || + language_code == "hi" || + language_code == "nl" || + language_code == "fr" || + language_code == "de") { + text = GetLanguageName(language_code) + L" - "; } + text += input_method::GetString(input_method.display_name); - // For the drop-down menu and tooltip, we'll show language names like - // "Chinese (Simplified)" and "Japanese", instead of input method names - // like "Pinyin" and "Mozc". - if (text.empty()) { - text = GetLanguageName(language_code); - if (add_method_name) { - text += L" - "; - text += input_method::GetString(input_method.display_name); - } - } DCHECK(!text.empty()); return text; } void LanguageMenuButton::RegisterPrefs(PrefService* local_state) { - local_state->RegisterStringPref(kPreferredKeyboardLayout, ""); + local_state->RegisterStringPref(language_prefs::kPreferredKeyboardLayout, + ""); } void LanguageMenuButton::Observe(NotificationType type, diff --git a/chrome/browser/chromeos/status/language_menu_button.h b/chrome/browser/chromeos/status/language_menu_button.h index dcafac1..7610e8c 100644 --- a/chrome/browser/chromeos/status/language_menu_button.h +++ b/chrome/browser/chromeos/status/language_menu_button.h @@ -4,11 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_LANGUAGE_MENU_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_LANGUAGE_MENU_BUTTON_H_ +#pragma once #include "app/menus/simple_menu_model.h" #include "chrome/browser/chromeos/cros/input_method_library.h" #include "chrome/browser/chromeos/status/status_area_button.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" @@ -24,7 +25,7 @@ class StatusAreaHost; // The language menu button in the status area. // This class will handle getting the IME/XKB status and populating the menu. -class LanguageMenuButton : public views::MenuButton, +class LanguageMenuButton : public StatusAreaButton, public views::ViewMenuDelegate, public menus::MenuModel, public InputMethodLibrary::Observer, @@ -69,15 +70,14 @@ class LanguageMenuButton : public views::MenuButton, // Converts an InputMethodDescriptor object into human readable string. // Returns a string for the drop-down menu and the tooltip for the indicator. - static std::wstring GetTextForMenu( - const InputMethodDescriptor& input_method, bool add_method_name); + static std::wstring GetTextForMenu(const InputMethodDescriptor& input_method); // Registers input method preferences for the login screen. static void RegisterPrefs(PrefService* local_state); protected: // views::View implementation. - virtual void LocaleChanged(); + virtual void OnLocaleChanged(); private: // views::ViewMenuDelegate implementation. @@ -114,9 +114,6 @@ class LanguageMenuButton : public views::MenuButton, StringPrefMember previous_input_method_pref_; StringPrefMember current_input_method_pref_; - // Languages that need the input method name displayed. - std::set<std::string> need_method_name_; - // We borrow menus::SimpleMenuModel implementation to maintain the current // content of the pop-up menu. The menus::MenuModel is implemented using this // |model_|. diff --git a/chrome/browser/chromeos/status/language_menu_button_browsertest.cc b/chrome/browser/chromeos/status/language_menu_button_browsertest.cc index 452e598..2176997 100644 --- a/chrome/browser/chromeos/status/language_menu_button_browsertest.cc +++ b/chrome/browser/chromeos/status/language_menu_button_browsertest.cc @@ -23,8 +23,8 @@ class LanguageMenuButtonTest : public CrosInProcessBrowserTest { } virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - SetStatusAreaMocksExpectations(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->SetStatusAreaMocksExpectations(); } LanguageMenuButton* GetLanguageMenuButton() { diff --git a/chrome/browser/chromeos/status/language_menu_button_unittest.cc b/chrome/browser/chromeos/status/language_menu_button_unittest.cc index 2f4044a..b760fbc 100644 --- a/chrome/browser/chromeos/status/language_menu_button_unittest.cc +++ b/chrome/browser/chromeos/status/language_menu_button_unittest.cc @@ -20,7 +20,8 @@ TEST(LanguageMenuButtonTest, GetTextForIndicatorTest) { } { InputMethodDescriptor desc("hangul", "Korean", "us", "ko"); - EXPECT_EQ(L"KO", LanguageMenuButton::GetTextForIndicator(desc)); + EXPECT_EQ(UTF8ToWide("\xed\x95\x9c"), + LanguageMenuButton::GetTextForIndicator(desc)); } { InputMethodDescriptor desc("invalid-id", "unregistered string", "us", "xx"); @@ -30,7 +31,7 @@ TEST(LanguageMenuButtonTest, GetTextForIndicatorTest) { // Test special cases. { - InputMethodDescriptor desc("xkb:us:dvorak:eng", "Dvorak", "us", "us"); + InputMethodDescriptor desc("xkb:us:dvorak:eng", "Dvorak", "us", "eng"); EXPECT_EQ(L"DV", LanguageMenuButton::GetTextForIndicator(desc)); } { @@ -60,39 +61,97 @@ TEST(LanguageMenuButtonTest, GetTextForIndicatorTest) { } { InputMethodDescriptor desc("m17n:zh:quick", "Quick", "us", "zh-TW"); - EXPECT_EQ(UTF8ToWide("TW"), - LanguageMenuButton::GetTextForIndicator(desc)); - } - { - InputMethodDescriptor desc("m17n:t:latn-pre", "latn-pre", "us", "t"); - EXPECT_EQ(L"LAT", + EXPECT_EQ(UTF8ToWide("\xe9\x80\x9f"), LanguageMenuButton::GetTextForIndicator(desc)); } } -TEST(LanguageMenuButtonTest, GetTextForTooltipTest) { - const bool kAddMethodName = true; + +// Test whether the function returns language name for non-ambiguous languages. +TEST(LanguageMenuButtonTest, GetTextForMenuTest) { + // For most languages input method or keyboard layout name is returned. + // See below for exceptions. { InputMethodDescriptor desc("m17n:fa:isiri", "isiri (m17n)", "us", "fa"); - EXPECT_EQ(L"Persian - Persian input method (ISIRI 2901 layout)", - LanguageMenuButton::GetTextForMenu(desc, kAddMethodName)); + EXPECT_EQ(L"Persian input method (ISIRI 2901 layout)", + LanguageMenuButton::GetTextForMenu(desc)); } { InputMethodDescriptor desc("hangul", "Korean", "us", "ko"); - EXPECT_EQ(L"Korean - Korean input method", - LanguageMenuButton::GetTextForMenu(desc, kAddMethodName)); + EXPECT_EQ(L"Korean input method", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("m17n:vi:tcvn", "tcvn (m17n)", "us", "vi"); + EXPECT_EQ(L"Vietnamese input method (TCVN6064)", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("mozc", "Mozc (US keyboard layout)", "us", "ja"); + EXPECT_EQ(L"Japanese input method (for US keyboard)", + LanguageMenuButton::GetTextForMenu(desc)); } { + InputMethodDescriptor desc("xkb:jp::jpn", "Japan", "jp", "jpn"); + EXPECT_EQ(L"Japanese keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:us:dvorak:eng", "USA - Dvorak", + "us(dvorak)", "eng"); + EXPECT_EQ(L"English (Dvorak)", + LanguageMenuButton::GetTextForMenu(desc)); + } + + // For Arabic, Dutch, French, German and Hindi, + // "language - keyboard layout" pair is returned. + { + InputMethodDescriptor desc("m17n:ar:kbd", "kbd (m17n)", "us", "ar"); + EXPECT_EQ(L"Arabic - Standard input method", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:nl::nld", "Netherlands", "nl", "nld"); + EXPECT_EQ(L"Dutch - Dutch keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:be::nld", "Belgium", "be", "nld"); + EXPECT_EQ(L"Dutch - Belgian keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:fr::fra", "France", "fr", "fra"); + EXPECT_EQ(L"French - French keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:be::fra", "Belgium", "be", "fra"); + EXPECT_EQ(L"French - Belgian keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:de::ger", "Germany", "de", "ger"); + EXPECT_EQ(L"German - German keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("xkb:be::ger", "Belgium", "be", "ger"); + EXPECT_EQ(L"German - Belgian keyboard layout", + LanguageMenuButton::GetTextForMenu(desc)); + } + { + InputMethodDescriptor desc("m17n:hi:itrans", "itrans (m17n)", "us", "hi"); + EXPECT_EQ(L"Hindi - Standard input method", + LanguageMenuButton::GetTextForMenu(desc)); + } + + { InputMethodDescriptor desc("invalid-id", "unregistered string", "us", "xx"); // You can safely ignore the "Resouce ID is not found for: unregistered // string" error. - EXPECT_EQ(L"xx - unregistered string", - LanguageMenuButton::GetTextForMenu(desc, kAddMethodName)); - } - { - InputMethodDescriptor desc("m17n:t:latn-pre", "latn-pre", "us", "t"); - EXPECT_EQ(L"latn-pre", - LanguageMenuButton::GetTextForMenu(desc, kAddMethodName)); + EXPECT_EQ(L"unregistered string", + LanguageMenuButton::GetTextForMenu(desc)); } } diff --git a/chrome/browser/chromeos/status/network_menu_button.cc b/chrome/browser/chromeos/status/network_menu_button.cc index 7ea165e..9ed0854 100644 --- a/chrome/browser/chromeos/status/network_menu_button.cc +++ b/chrome/browser/chromeos/status/network_menu_button.cc @@ -4,19 +4,19 @@ #include "chrome/browser/chromeos/status/network_menu_button.h" +#include <algorithm> #include <limits> #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/options/network_config_view.h" #include "chrome/browser/chromeos/status/status_area_host.h" #include "gfx/canvas_skia.h" -#include "gfx/skbitmap_operations.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "views/widget/widget.h" #include "views/window/window.h" namespace chromeos { @@ -25,13 +25,12 @@ namespace chromeos { // NetworkMenuButton // static -const int NetworkMenuButton::kNumWifiImages = 9; const int NetworkMenuButton::kThrobDuration = 1000; NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host) : StatusAreaButton(this), + NetworkMenu(), host_(host), - ALLOW_THIS_IN_INITIALIZER_LIST(network_menu_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(animation_connecting_(this)) { animation_connecting_.SetThrobDuration(kThrobDuration); animation_connecting_.SetTweenType(Tween::LINEAR); @@ -44,142 +43,6 @@ NetworkMenuButton::~NetworkMenuButton() { } //////////////////////////////////////////////////////////////////////////////// -// NetworkMenuButton, menus::MenuModel implementation: - -int NetworkMenuButton::GetItemCount() const { - return static_cast<int>(menu_items_.size()); -} - -menus::MenuModel::ItemType NetworkMenuButton::GetTypeAt(int index) const { - return menu_items_[index].type; -} - -string16 NetworkMenuButton::GetLabelAt(int index) const { - return menu_items_[index].label; -} - -const gfx::Font* NetworkMenuButton::GetLabelFontAt(int index) const { - return (menu_items_[index].flags & FLAG_ASSOCIATED) ? - &ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BoldFont) : - NULL; -} - -bool NetworkMenuButton::IsItemCheckedAt(int index) const { - // All menus::MenuModel::TYPE_CHECK menu items are checked. - return true; -} - -bool NetworkMenuButton::GetIconAt(int index, SkBitmap* icon) const { - if (!menu_items_[index].icon.empty()) { - *icon = menu_items_[index].icon; - return true; - } - return false; -} - -bool NetworkMenuButton::IsEnabledAt(int index) const { - return !(menu_items_[index].flags & FLAG_DISABLED); -} - -void NetworkMenuButton::ActivatedAt(int index) { - // When we are refreshing the menu, ignore menu item activation. - if (refreshing_menu_) - return; - - NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); - int flags = menu_items_[index].flags; - if (flags & FLAG_OPTIONS) { - host_->OpenButtonOptions(this); - } else if (flags & FLAG_TOGGLE_ETHERNET) { - cros->EnableEthernetNetworkDevice(!cros->ethernet_enabled()); - } else if (flags & FLAG_TOGGLE_WIFI) { - cros->EnableWifiNetworkDevice(!cros->wifi_enabled()); - } else if (flags & FLAG_TOGGLE_CELLULAR) { - cros->EnableCellularNetworkDevice(!cros->cellular_enabled()); - } else if (flags & FLAG_TOGGLE_OFFLINE) { - cros->EnableOfflineMode(!cros->offline_mode()); - } else if (flags & FLAG_OTHER_NETWORK) { - NetworkConfigView* view = new NetworkConfigView(); - view->set_browser_mode(host_->IsBrowserMode()); - views::Window* window = views::Window::CreateChromeWindow( - host_->GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); - view->SetLoginTextfieldFocus(); - } else if (flags & FLAG_ETHERNET) { - if (cros->ethernet_connected()) { - NetworkConfigView* view = new NetworkConfigView(cros->ethernet_network()); - view->set_browser_mode(host_->IsBrowserMode()); - views::Window* window = views::Window::CreateChromeWindow( - host_->GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); - } - } else if (flags & FLAG_WIFI) { - WifiNetwork wifi; - bool wifi_exists = cros->FindWifiNetworkByPath( - menu_items_[index].wireless_path, &wifi); - if (!wifi_exists) { - // If we are attempting to connect to a network that no longer exists, - // display a notification. - // TODO(stevenjb): Show notification. - } else if (wifi.name() == cros->wifi_name()) { - if (cros->wifi_connected()) { - // If we are already connected, open the config dialog. - NetworkConfigView* view = new NetworkConfigView(wifi, false); - view->set_browser_mode(host_->IsBrowserMode()); - views::Window* window = views::Window::CreateChromeWindow( - host_->GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); - } else { - // TODO(stevenjb): Connection in progress. Show dialog? - } - } else { - // If wifi network is not encrypted, then directly connect. - // Otherwise, we open password dialog window. - if (!wifi.encrypted()) { - cros->ConnectToWifiNetwork(wifi, std::string(), - std::string(), std::string()); - } else { - NetworkConfigView* view = new NetworkConfigView(wifi, true); - view->set_browser_mode(host_->IsBrowserMode()); - views::Window* window = views::Window::CreateChromeWindow( - host_->GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); - view->SetLoginTextfieldFocus(); - } - } - } else if (flags & FLAG_CELLULAR) { - CellularNetwork cellular; - bool cellular_exists = cros->FindCellularNetworkByPath( - menu_items_[index].wireless_path, &cellular); - - if (!cellular_exists) { - // If we are attempting to connect to a network that no longer exists, - // display a notification. - // TODO(stevenjb): Show notification. - } else if (cellular.name() == cros->cellular_name()) { - // If clicked on a network that we are already connected to or we are - // currently trying to connect to, then open config dialog. - if (cros->cellular_connected()) { - NetworkConfigView* view = new NetworkConfigView(cellular); - view->set_browser_mode(host_->IsBrowserMode()); - views::Window* window = views::Window::CreateChromeWindow( - host_->GetNativeWindow(), gfx::Rect(), view); - window->SetIsAlwaysOnTop(true); - window->Show(); - } else { - // TODO(stevenjb): Connection in progress. Show dialog? - } - } else { - cros->ConnectToCellularNetwork(cellular); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// // NetworkMenuButton, AnimationDelegate implementation: void NetworkMenuButton::AnimationProgressed(const Animation* animation) { @@ -221,144 +84,6 @@ void NetworkMenuButton::DrawIcon(gfx::Canvas* canvas) { canvas->DrawBitmapInt(IconForDisplay(icon(), badge()), 0, 0); } -// Override the DrawIcon method to draw the wifi icon. -// The wifi icon is composed of 1 or more alpha-blended icons to show the -// network strength. We also draw an animation for when there's upload/download -// traffic. -/* TODO(chocobo): Add this code back in when UI is finalized. -void NetworkMenuButton::DrawIcon(gfx::Canvas* canvas) { - - // First draw the base icon. - canvas->DrawBitmapInt(icon(), 0, 0); - - // If wifi, we draw the wifi signal bars. - NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); - if (cros->wifi_connecting() || - (!cros->ethernet_connected() && cros->wifi_connected())) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - // We want a value between 0-1. - // 0 reperesents no signal and 1 represents full signal strength. - double value = cros->wifi_connecting() ? - animation_connecting_.GetCurrentValue() : - cros->wifi_strength() / 100.0; - if (value < 0) - value = 0; - else if (value > 1) - value = 1; - - // If we are animating network traffic and not connecting, then we need to - // figure out if we are to also draw the extra image. - int downloading_index = -1; - int uploading_index = -1; - if (!animation_connecting_.is_animating()) { - // For network animation, we only show animation in one direction. - // So when we are hiding, we just use 1 minus the value. - // We have kNumWifiImages + 1 number of states. For the first state, where - // we are not adding any images, we set the index to -1. - if (animation_downloading_.is_animating()) { - double value_downloading = animation_downloading_.IsShowing() ? - animation_downloading_.GetCurrentValue() : - 1.0 - animation_downloading_.GetCurrentValue(); - downloading_index = static_cast<int>(value_downloading * - nextafter(static_cast<float>(kNumWifiImages + 1), 0)) - 1; - } - if (animation_uploading_.is_animating()) { - double value_uploading = animation_uploading_.IsShowing() ? - animation_uploading_.GetCurrentValue() : - 1.0 - animation_uploading_.GetCurrentValue(); - uploading_index = static_cast<int>(value_uploading * - nextafter(static_cast<float>(kNumWifiImages + 1), 0)) - 1; - } - } - - // We need to determine opacity for each of the kNumWifiImages images. - // We split the range (0-1) into equal ranges per kNumWifiImages images. - // For example if kNumWifiImages is 3, then [0-0.33) is the first image and - // [0.33-0.66) is the second image and [0.66-1] is the last image. - // For each of the image: - // If value < the range of this image, draw at kMinOpacity opacity. - // If value > the range of this image, draw at kMaxOpacity-1 opacity. - // If value within the range of this image, draw at an opacity value - // between kMinOpacity and kMaxOpacity-1 relative to where in the range - // value is at. - // NOTE: Use an array rather than just calculating a resource number to - // avoid creating implicit ordering dependencies on the resource values. - static const int kWifiUpImages[kNumWifiImages] = { - IDR_STATUSBAR_WIFI_UP1, - IDR_STATUSBAR_WIFI_UP2, - IDR_STATUSBAR_WIFI_UP3, - IDR_STATUSBAR_WIFI_UP4, - IDR_STATUSBAR_WIFI_UP5, - IDR_STATUSBAR_WIFI_UP6, - IDR_STATUSBAR_WIFI_UP7, - IDR_STATUSBAR_WIFI_UP8, - IDR_STATUSBAR_WIFI_UP9, - }; - static const int kWifiUpPImages[kNumWifiImages] = { - IDR_STATUSBAR_WIFI_UP1P, - IDR_STATUSBAR_WIFI_UP2P, - IDR_STATUSBAR_WIFI_UP3P, - IDR_STATUSBAR_WIFI_UP4P, - IDR_STATUSBAR_WIFI_UP5P, - IDR_STATUSBAR_WIFI_UP6P, - IDR_STATUSBAR_WIFI_UP7P, - IDR_STATUSBAR_WIFI_UP8P, - IDR_STATUSBAR_WIFI_UP9P, - }; - static const int kWifiDownImages[kNumWifiImages] = { - IDR_STATUSBAR_WIFI_DOWN1, - IDR_STATUSBAR_WIFI_DOWN2, - IDR_STATUSBAR_WIFI_DOWN3, - IDR_STATUSBAR_WIFI_DOWN4, - IDR_STATUSBAR_WIFI_DOWN5, - IDR_STATUSBAR_WIFI_DOWN6, - IDR_STATUSBAR_WIFI_DOWN7, - IDR_STATUSBAR_WIFI_DOWN8, - IDR_STATUSBAR_WIFI_DOWN9, - }; - static const int kWifiDownPImages[kNumWifiImages] = { - IDR_STATUSBAR_WIFI_DOWN1P, - IDR_STATUSBAR_WIFI_DOWN2P, - IDR_STATUSBAR_WIFI_DOWN3P, - IDR_STATUSBAR_WIFI_DOWN4P, - IDR_STATUSBAR_WIFI_DOWN5P, - IDR_STATUSBAR_WIFI_DOWN6P, - IDR_STATUSBAR_WIFI_DOWN7P, - IDR_STATUSBAR_WIFI_DOWN8P, - IDR_STATUSBAR_WIFI_DOWN9P, - }; - - double value_per_image = 1.0 / kNumWifiImages; - SkPaint paint; - for (int i = 0; i < kNumWifiImages; i++) { - if (value > value_per_image) { - paint.setAlpha(kMaxOpacity - 1); - value -= value_per_image; - } else { - // Map value between 0 and value_per_image to [kMinOpacity,kMaxOpacity). - paint.setAlpha(kMinOpacity + static_cast<int>(value / value_per_image * - nextafter(static_cast<float>(kMaxOpacity - kMinOpacity), 0))); - // For following iterations, we want to draw at kMinOpacity. - // So we set value to 0 here. - value = 0; - } - canvas->DrawBitmapInt(*rb.GetBitmapNamed(kWifiUpImages[i]), 0, 0, paint); - canvas->DrawBitmapInt(*rb.GetBitmapNamed(kWifiDownImages[i]), 0, 0, - paint); - - // Draw network traffic downloading/uploading image if necessary. - if (i == downloading_index) { - canvas->DrawBitmapInt(*rb.GetBitmapNamed(kWifiDownPImages[i]), 0, 0, - paint); - } - if (i == uploading_index) { - canvas->DrawBitmapInt(*rb.GetBitmapNamed(kWifiUpPImages[i]), 0, 0, - paint); - } - } - } -} -*/ //////////////////////////////////////////////////////////////////////////////// // NetworkMenuButton, NetworkLibrary::Observer implementation: @@ -372,19 +97,37 @@ void NetworkMenuButton::NetworkChanged(NetworkLibrary* cros) { animation_connecting_.StartThrobbing(std::numeric_limits<int>::max()); SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS1)); } + std::string network_name = cros->wifi_connecting() ? + cros->wifi_name() : cros->cellular_name(); + SetTooltipText( + l10n_util::GetStringF(IDS_STATUSBAR_NETWORK_CONNECTING_TOOLTIP, + UTF8ToWide(network_name))); } else { // Stop connecting animation since we are not connecting. animation_connecting_.Stop(); // Always show the higher priority connection first. Ethernet then wifi. - if (cros->ethernet_connected()) + if (cros->ethernet_connected()) { SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED)); - else if (cros->wifi_connected()) + SetTooltipText( + l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET))); + } else if (cros->wifi_connected()) { SetIcon(IconForNetworkStrength(cros->wifi_strength(), false)); - else if (cros->cellular_connected()) + SetTooltipText(l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + UTF8ToWide(cros->wifi_name()))); + } else if (cros->cellular_connected()) { SetIcon(IconForNetworkStrength(cros->cellular_strength(), false)); - else + SetTooltipText(l10n_util::GetStringF( + IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP, + UTF8ToWide(cros->cellular_name()))); + } else { SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); + SetTooltipText(l10n_util::GetString( + IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)); + } } if (!cros->Connected() && !cros->Connecting()) { @@ -400,211 +143,34 @@ void NetworkMenuButton::NetworkChanged(NetworkLibrary* cros) { } else { SetIcon(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0)); SetBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING)); + SetTooltipText(l10n_util::GetString( + IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)); } SchedulePaint(); } -void NetworkMenuButton::NetworkTraffic(NetworkLibrary* cros, int traffic_type) { -/* TODO(chocobo): Add this code back in when network traffic UI is finalized. - if (!cros->ethernet_connected() && cros->wifi_connected() && - !cros->wifi_connecting()) { - // For downloading/uploading animation, we want to force at least one cycle - // so that it looks smooth. And if we keep downloading/uploading, we will - // keep calling StartThrobbing which will update the cycle count back to 2. - if (traffic_type & TRAFFIC_DOWNLOAD) - animation_downloading_.StartThrobbing(2); - if (traffic_type & TRAFFIC_UPLOAD) - animation_uploading_.StartThrobbing(2); - } - */ -} - void NetworkMenuButton::SetBadge(const SkBitmap& badge) { badge_ = badge; } -// static -SkBitmap NetworkMenuButton::IconForNetworkStrength(int strength, bool black) { - // Compose wifi icon by superimposing various icons. - // NOTE: Use an array rather than just calculating a resource number to avoid - // creating implicit ordering dependencies on the resource values. - static const int kBarsImages[kNumWifiImages] = { - IDR_STATUSBAR_NETWORK_BARS1, - IDR_STATUSBAR_NETWORK_BARS2, - IDR_STATUSBAR_NETWORK_BARS3, - IDR_STATUSBAR_NETWORK_BARS4, - IDR_STATUSBAR_NETWORK_BARS5, - IDR_STATUSBAR_NETWORK_BARS6, - IDR_STATUSBAR_NETWORK_BARS7, - IDR_STATUSBAR_NETWORK_BARS8, - IDR_STATUSBAR_NETWORK_BARS9, - }; - static const int kBarsBlackImages[kNumWifiImages] = { - IDR_STATUSBAR_NETWORK_BARS1_BLACK, - IDR_STATUSBAR_NETWORK_BARS2_BLACK, - IDR_STATUSBAR_NETWORK_BARS3_BLACK, - IDR_STATUSBAR_NETWORK_BARS4_BLACK, - IDR_STATUSBAR_NETWORK_BARS5_BLACK, - IDR_STATUSBAR_NETWORK_BARS6_BLACK, - IDR_STATUSBAR_NETWORK_BARS7_BLACK, - IDR_STATUSBAR_NETWORK_BARS8_BLACK, - IDR_STATUSBAR_NETWORK_BARS9_BLACK, - }; +//////////////////////////////////////////////////////////////////////////////// +// NetworkMenuButton, NetworkMenu implementation: - int index = static_cast<int>(strength / 100.0 * - nextafter(static_cast<float>(kNumWifiImages), 0)); - index = std::max(std::min(index, kNumWifiImages - 1), 0); - return *ResourceBundle::GetSharedInstance().GetBitmapNamed( - black ? kBarsBlackImages[index] : kBarsImages[index]); +bool NetworkMenuButton::IsBrowserMode() const { + return host_->IsBrowserMode(); } -// static -SkBitmap NetworkMenuButton::IconForDisplay(SkBitmap icon, SkBitmap badge) { - // Icons are 24x24. - static const int kIconWidth = 24; - static const int kIconHeight = 24; - // Draw the network icon 3 pixels down to center it. - static const int kIconX = 0; - static const int kIconY = 3; - // Draw badge at (14,14). - static const int kBadgeX = 14; - static const int kBadgeY = 14; - - gfx::CanvasSkia canvas(kIconWidth, kIconHeight, false); - canvas.DrawBitmapInt(icon, kIconX, kIconY); - if (!badge.empty()) - canvas.DrawBitmapInt(badge, kBadgeX, kBadgeY); - return canvas.ExtractBitmap(); +gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const { + return host_->GetNativeWindow(); } -//////////////////////////////////////////////////////////////////////////////// -// NetworkMenuButton, views::ViewMenuDelegate implementation: - -void NetworkMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { - refreshing_menu_ = true; - NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); - cros->RequestWifiScan(); - cros->UpdateSystemInfo(); - InitMenuItems(); - network_menu_.Rebuild(); - network_menu_.UpdateStates(); - refreshing_menu_ = false; - network_menu_.RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); +void NetworkMenuButton::OpenButtonOptions() const { + host_->OpenButtonOptions(this); } -void NetworkMenuButton::InitMenuItems() { - menu_items_.clear(); - // Populate our MenuItems with the current list of wifi networks. - NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - // Ethernet - string16 label = l10n_util::GetStringUTF16( - IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); - SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); - SkBitmap badge = cros->ethernet_connecting() || cros->ethernet_connected() ? - SkBitmap() : *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED); - int flag = (cros->ethernet_connecting() || cros->ethernet_connected()) ? - FLAG_ETHERNET | FLAG_ASSOCIATED : FLAG_ETHERNET; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), std::string(), flag)); - - // Wifi - const WifiNetworkVector& wifi_networks = cros->wifi_networks(); - // Wifi networks ssids. - for (size_t i = 0; i < wifi_networks.size(); ++i) { - label = ASCIIToUTF16(wifi_networks[i].name()); - SkBitmap icon = IconForNetworkStrength(wifi_networks[i].strength(), true); - SkBitmap badge = wifi_networks[i].encrypted() ? - *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : SkBitmap(); - flag = (wifi_networks[i].name() == cros->wifi_name()) ? - FLAG_WIFI | FLAG_ASSOCIATED : FLAG_WIFI; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), wifi_networks[i].service_path(), flag)); - } - - // Cellular - const CellularNetworkVector& cell_networks = cros->cellular_networks(); - // Cellular networks ssids. - for (size_t i = 0; i < cell_networks.size(); ++i) { - label = ASCIIToUTF16(cell_networks[i].name()); - SkBitmap icon = IconForNetworkStrength(cell_networks[i].strength(), true); - // TODO(chocobo): Check cellular network 3g/edge. - SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); -// SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_EDGE); - flag = (cell_networks[i].name() == cros->cellular_name()) ? - FLAG_CELLULAR | FLAG_ASSOCIATED : FLAG_CELLULAR; - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - IconForDisplay(icon, badge), cell_networks[i].service_path(), flag)); - } - - // No networks available message. - if (wifi_networks.empty() && cell_networks.empty()) { - label = l10n_util::GetStringFUTF16(IDS_STATUSBAR_NETWORK_MENU_ITEM_INDENT, - l10n_util::GetStringUTF16(IDS_STATUSBAR_NO_NETWORKS_MESSAGE)); - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - SkBitmap(), std::string(), FLAG_DISABLED)); - } - - // Other networks - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, - l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS), - IconForDisplay(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0), - SkBitmap()), - std::string(), FLAG_OTHER_NETWORK)); - - if (cros->wifi_available() || cros->cellular_available()) { - // Separator. - menu_items_.push_back(MenuItem()); - - // Turn Wifi Off. (only if wifi available) - if (cros->wifi_available()) { - int id = cros->wifi_enabled() ? IDS_STATUSBAR_NETWORK_DEVICE_DISABLE : - IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; - label = l10n_util::GetStringFUTF16(id, - l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_WIFI)); - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - SkBitmap(), std::string(), FLAG_TOGGLE_WIFI)); - } - - // Turn Cellular Off. (only if cellular available) - if (cros->cellular_available()) { - int id = cros->cellular_enabled() ? IDS_STATUSBAR_NETWORK_DEVICE_DISABLE : - IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; - label = l10n_util::GetStringFUTF16(id, - l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CELLULAR)); - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - SkBitmap(), std::string(), FLAG_TOGGLE_CELLULAR)); - } - } - - // TODO(chocobo): Uncomment once we figure out how to do offline mode. - // Offline mode. -// menu_items_.push_back(MenuItem(cros->offline_mode() ? -// menus::MenuModel::TYPE_CHECK : menus::MenuModel::TYPE_COMMAND, -// l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OFFLINE_MODE), -// SkBitmap(), std::string(), FLAG_TOGGLE_OFFLINE)); - - if (cros->Connected() || host_->ShouldOpenButtonOptions(this)) { - // Separator. - menu_items_.push_back(MenuItem()); - - // IP address - if (cros->Connected()) { - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, - ASCIIToUTF16(cros->IPAddress()), SkBitmap(), - std::string(), FLAG_DISABLED)); - } - - // Network settings. - if (host_->ShouldOpenButtonOptions(this)) { - label = - l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OPEN_OPTIONS_DIALOG); - menu_items_.push_back(MenuItem(menus::MenuModel::TYPE_COMMAND, label, - SkBitmap(), std::string(), FLAG_OPTIONS)); - } - } +bool NetworkMenuButton::ShouldOpenButtonOptions() const { + return host_->ShouldOpenButtonOptions(this); } } // namespace chromeos diff --git a/chrome/browser/chromeos/status/network_menu_button.h b/chrome/browser/chromeos/status/network_menu_button.h index e5325ad..bb5ff96 100644 --- a/chrome/browser/chromeos/status/network_menu_button.h +++ b/chrome/browser/chromeos/status/network_menu_button.h @@ -4,19 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_BUTTON_H_ - -#include <string> -#include <vector> +#pragma once #include "app/throb_animation.h" #include "base/timer.h" #include "chrome/browser/chromeos/cros/network_library.h" -#include "chrome/browser/chromeos/options/network_config_view.h" +#include "chrome/browser/chromeos/status/network_menu.h" #include "chrome/browser/chromeos/status/status_area_button.h" -#include "views/controls/menu/menu_2.h" -#include "views/controls/menu/view_menu_delegate.h" - -class SkBitmap; namespace gfx { class Canvas; @@ -51,50 +45,17 @@ class StatusAreaHost; // <icon> will show the strength of the wifi/cellular networks. // The label will be BOLD if the network is currently connected. class NetworkMenuButton : public StatusAreaButton, - public views::ViewMenuDelegate, - public menus::MenuModel, + public NetworkMenu, public NetworkLibrary::Observer { public: explicit NetworkMenuButton(StatusAreaHost* host); virtual ~NetworkMenuButton(); - // menus::MenuModel implementation. - virtual bool HasIcons() const { return true; } - virtual int GetItemCount() const; - virtual menus::MenuModel::ItemType GetTypeAt(int index) const; - virtual int GetCommandIdAt(int index) const { return index; } - virtual string16 GetLabelAt(int index) const; - virtual bool IsLabelDynamicAt(int index) const { return true; } - virtual const gfx::Font* GetLabelFontAt(int index) const; - virtual bool GetAcceleratorAt(int index, - menus::Accelerator* accelerator) const { return false; } - virtual bool IsItemCheckedAt(int index) const; - virtual int GetGroupIdAt(int index) const { return 0; } - virtual bool GetIconAt(int index, SkBitmap* icon) const; - virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const { - return NULL; - } - virtual bool IsEnabledAt(int index) const; - virtual menus::MenuModel* GetSubmenuModelAt(int index) const { return NULL; } - virtual void HighlightChangedTo(int index) {} - virtual void ActivatedAt(int index); - virtual void MenuWillShow() {} - // AnimationDelegate implementation. virtual void AnimationProgressed(const Animation* animation); // NetworkLibrary::Observer implementation. virtual void NetworkChanged(NetworkLibrary* obj); - virtual void NetworkTraffic(NetworkLibrary* cros, int traffic_type); - - // Returns the Icon for a network strength between 0 and 100. - // |black| is used to specify whether to return a black icon for display - // on a light background or a white icon for display on a dark background. - static SkBitmap IconForNetworkStrength(int strength, bool black); - - // This method will convert the |icon| bitmap to the correct size for display. - // If the |badge| icon is not empty, it will draw that on top of the icon. - static SkBitmap IconForDisplay(SkBitmap icon, SkBitmap badge); // Sets the badge icon. void SetBadge(const SkBitmap& badge); @@ -105,62 +66,16 @@ class NetworkMenuButton : public StatusAreaButton, virtual void DrawPressed(gfx::Canvas* canvas); virtual void DrawIcon(gfx::Canvas* canvas); - private: - enum MenuItemFlags { - FLAG_DISABLED = 1 << 0, - FLAG_TOGGLE_ETHERNET = 1 << 1, - FLAG_TOGGLE_WIFI = 1 << 2, - FLAG_TOGGLE_CELLULAR = 1 << 3, - FLAG_TOGGLE_OFFLINE = 1 << 4, - FLAG_ASSOCIATED = 1 << 5, - FLAG_ETHERNET = 1 << 6, - FLAG_WIFI = 1 << 7, - FLAG_CELLULAR = 1 << 8, - FLAG_OPTIONS = 1 << 9, - FLAG_OTHER_NETWORK = 1 << 10, - }; - - struct MenuItem { - MenuItem() - : type(menus::MenuModel::TYPE_SEPARATOR), - flags(0) {} - MenuItem(menus::MenuModel::ItemType type, string16 label, SkBitmap icon, - const std::string& wireless_path, int flags) - : type(type), - label(label), - icon(icon), - wireless_path(wireless_path), - flags(flags) {} - - menus::MenuModel::ItemType type; - string16 label; - SkBitmap icon; - std::string wireless_path; - int flags; - }; - typedef std::vector<MenuItem> MenuItemVector; - - // views::ViewMenuDelegate implementation. - virtual void RunMenu(views::View* source, const gfx::Point& pt); - - // Called by RunMenu to initialize our list of menu items. - void InitMenuItems(); - - // Set to true if we are currently refreshing the menu. - bool refreshing_menu_; - - // The number of wifi strength images. - static const int kNumWifiImages; - - // Our menu items. - MenuItemVector menu_items_; + // NetworkMenu implementation: + virtual bool IsBrowserMode() const; + virtual gfx::NativeWindow GetNativeWindow() const; + virtual void OpenButtonOptions() const; + virtual bool ShouldOpenButtonOptions() const; + private: // The status area host, StatusAreaHost* host_; - // The network menu. - views::Menu2 network_menu_; - // A badge icon displayed on top of the icon. SkBitmap badge_; diff --git a/chrome/browser/chromeos/status/power_menu_button.cc b/chrome/browser/chromeos/status/power_menu_button.cc index de10159..de942cb 100644 --- a/chrome/browser/chromeos/status/power_menu_button.cc +++ b/chrome/browser/chromeos/status/power_menu_button.cc @@ -6,7 +6,9 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/string_number_conversions.h" #include "base/time.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" @@ -22,9 +24,13 @@ const int PowerMenuButton::kNumPowerImages = 12; PowerMenuButton::PowerMenuButton() : StatusAreaButton(this), - ALLOW_THIS_IN_INITIALIZER_LIST(power_menu_(this)), - icon_id_(-1) { - UpdateIcon(); + battery_is_present_(false), + line_power_on_(false), + battery_fully_charged_(false), + battery_percentage_(0.0), + icon_id_(-1), + ALLOW_THIS_IN_INITIALIZER_LIST(power_menu_(this)) { + UpdateIconAndLabelInfo(); CrosLibrary::Get()->GetPowerLibrary()->AddObserver(this); } @@ -44,45 +50,44 @@ menus::MenuModel::ItemType PowerMenuButton::GetTypeAt(int index) const { } string16 PowerMenuButton::GetLabelAt(int index) const { - PowerLibrary* cros = CrosLibrary::Get()->GetPowerLibrary(); // The first item shows the percentage of battery left. if (index == 0) { - // If fully charged, always show 100% even if internal number is a bit less. - double percent = cros->battery_fully_charged() ? 100 : - cros->battery_percentage(); return l10n_util::GetStringFUTF16(IDS_STATUSBAR_BATTERY_PERCENTAGE, - IntToString16(static_cast<int>(percent))); - } - - // The second item shows the battery is charged if it is. - if (cros->battery_fully_charged()) - return l10n_util::GetStringUTF16(IDS_STATUSBAR_BATTERY_IS_CHARGED); - - // If battery is in an intermediate charge state, we show how much time left. - base::TimeDelta time = cros->line_power_on() ? cros->battery_time_to_full() : - cros->battery_time_to_empty(); - if (time.InSeconds() == 0) { - // If time is 0, then that means we are still calculating how much time. - // Depending if line power is on, we either show a message saying that we - // are calculating time until full or calculating remaining time. - int msg = cros->line_power_on() ? - IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_FULL : - IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_EMPTY; - return l10n_util::GetStringUTF16(msg); + base::IntToString16(static_cast<int>(battery_percentage_))); + } else if (index == 1) { + // The second item shows the battery is charged if it is. + if (battery_fully_charged_) + return l10n_util::GetStringUTF16(IDS_STATUSBAR_BATTERY_IS_CHARGED); + + // If battery is in an intermediate charge state, show how much time left. + base::TimeDelta time = line_power_on_ ? battery_time_to_full_ : + battery_time_to_empty_; + if (time.InSeconds() == 0) { + // If time is 0, then that means we are still calculating how much time. + // Depending if line power is on, we either show a message saying that we + // are calculating time until full or calculating remaining time. + int msg = line_power_on_ ? + IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_FULL : + IDS_STATUSBAR_BATTERY_CALCULATING_TIME_UNTIL_EMPTY; + return l10n_util::GetStringUTF16(msg); + } else { + // Depending if line power is on, we either show a message saying XX:YY + // until full or XX:YY remaining where XX is number of hours and YY is + // number of minutes. + int msg = line_power_on_ ? IDS_STATUSBAR_BATTERY_TIME_UNTIL_FULL : + IDS_STATUSBAR_BATTERY_TIME_UNTIL_EMPTY; + int hour = time.InHours(); + int min = (time - base::TimeDelta::FromHours(hour)).InMinutes(); + string16 hour_str = base::IntToString16(hour); + string16 min_str = base::IntToString16(min); + // Append a "0" before the minute if it's only a single digit. + if (min < 10) + min_str = ASCIIToUTF16("0") + min_str; + return l10n_util::GetStringFUTF16(msg, hour_str, min_str); + } } else { - // Depending if line power is on, we either show a message saying XX:YY - // until full or XX:YY remaining where XX is number of hours and YY is - // number of minutes. - int msg = cros->line_power_on() ? IDS_STATUSBAR_BATTERY_TIME_UNTIL_FULL : - IDS_STATUSBAR_BATTERY_TIME_UNTIL_EMPTY; - int hour = time.InHours(); - int min = (time - base::TimeDelta::FromHours(hour)).InMinutes(); - string16 hour_str = IntToString16(hour); - string16 min_str = IntToString16(min); - // Append a "0" before the minute if it's only a single digit. - if (min < 10) - min_str = ASCIIToUTF16("0") + min_str; - return l10n_util::GetStringFUTF16(msg, hour_str, min_str); + NOTREACHED(); + return string16(); } } @@ -99,7 +104,7 @@ void PowerMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { // PowerMenuButton, PowerLibrary::Observer implementation: void PowerMenuButton::PowerChanged(PowerLibrary* obj) { - UpdateIcon(); + UpdateIconAndLabelInfo(); } //////////////////////////////////////////////////////////////////////////////// @@ -120,59 +125,74 @@ void PowerMenuButton::DrawPowerIcon(gfx::Canvas* canvas, SkBitmap icon) { canvas->DrawBitmapInt(icon, 0, kIconVerticalPadding); } -void PowerMenuButton::UpdateIcon() { +void PowerMenuButton::UpdateIconAndLabelInfo() { PowerLibrary* cros = CrosLibrary::Get()->GetPowerLibrary(); - icon_id_ = IDR_STATUSBAR_BATTERY_UNKNOWN; - if (CrosLibrary::Get()->EnsureLoaded()) { - if (!cros->battery_is_present()) { - icon_id_ = IDR_STATUSBAR_BATTERY_MISSING; - } else if (cros->line_power_on() && cros->battery_fully_charged()) { - icon_id_ = IDR_STATUSBAR_BATTERY_CHARGED; - } else { - // Get the power image depending on battery percentage. Percentage is - // from 0 to 100, so we need to convert that to 0 to kNumPowerImages - 1. - // NOTE: Use an array rather than just calculating a resource number to - // avoid creating implicit ordering dependencies on the resource values. - static const int kChargingImages[kNumPowerImages] = { - IDR_STATUSBAR_BATTERY_CHARGING_1, - IDR_STATUSBAR_BATTERY_CHARGING_2, - IDR_STATUSBAR_BATTERY_CHARGING_3, - IDR_STATUSBAR_BATTERY_CHARGING_4, - IDR_STATUSBAR_BATTERY_CHARGING_5, - IDR_STATUSBAR_BATTERY_CHARGING_6, - IDR_STATUSBAR_BATTERY_CHARGING_7, - IDR_STATUSBAR_BATTERY_CHARGING_8, - IDR_STATUSBAR_BATTERY_CHARGING_9, - IDR_STATUSBAR_BATTERY_CHARGING_10, - IDR_STATUSBAR_BATTERY_CHARGING_11, - IDR_STATUSBAR_BATTERY_CHARGING_12, - }; - static const int kDischargingImages[kNumPowerImages] = { - IDR_STATUSBAR_BATTERY_DISCHARGING_1, - IDR_STATUSBAR_BATTERY_DISCHARGING_2, - IDR_STATUSBAR_BATTERY_DISCHARGING_3, - IDR_STATUSBAR_BATTERY_DISCHARGING_4, - IDR_STATUSBAR_BATTERY_DISCHARGING_5, - IDR_STATUSBAR_BATTERY_DISCHARGING_6, - IDR_STATUSBAR_BATTERY_DISCHARGING_7, - IDR_STATUSBAR_BATTERY_DISCHARGING_8, - IDR_STATUSBAR_BATTERY_DISCHARGING_9, - IDR_STATUSBAR_BATTERY_DISCHARGING_10, - IDR_STATUSBAR_BATTERY_DISCHARGING_11, - IDR_STATUSBAR_BATTERY_DISCHARGING_12, - }; - - // If fully charged, always show 100% even if percentage is a bit less. - double percent = cros->battery_fully_charged() ? - 100 : cros->battery_percentage(); - int index = static_cast<int>(percent / 100.0 * - nextafter(static_cast<float>(kNumPowerImages), 0)); - index = std::max(std::min(index, kNumPowerImages - 1), 0); - icon_id_ = cros->line_power_on() ? - kChargingImages[index] : kDischargingImages[index]; - } + if (!cros) + return; + + bool cros_loaded = CrosLibrary::Get()->EnsureLoaded(); + if (cros_loaded) { + battery_is_present_ = cros->battery_is_present(); + line_power_on_ = cros->line_power_on(); + battery_fully_charged_ = cros->battery_fully_charged(); + battery_percentage_ = cros->battery_percentage(); + // If fully charged, always show 100% even if internal number is a bit less. + // Note: we always call cros->battery_percentage() for test predictability. + if (battery_fully_charged_) + battery_percentage_ = 100.0; + battery_time_to_full_ = cros->battery_time_to_full(); + battery_time_to_empty_ = cros->battery_time_to_empty(); + } + + if (!cros_loaded) { + icon_id_ = IDR_STATUSBAR_BATTERY_UNKNOWN; + } else if (!battery_is_present_) { + icon_id_ = IDR_STATUSBAR_BATTERY_MISSING; + } else if (line_power_on_ && battery_fully_charged_) { + icon_id_ = IDR_STATUSBAR_BATTERY_CHARGED; + } else { + // Get the power image depending on battery percentage. Percentage is + // from 0 to 100, so we need to convert that to 0 to kNumPowerImages - 1. + // NOTE: Use an array rather than just calculating a resource number to + // avoid creating implicit ordering dependencies on the resource values. + static const int kChargingImages[kNumPowerImages] = { + IDR_STATUSBAR_BATTERY_CHARGING_1, + IDR_STATUSBAR_BATTERY_CHARGING_2, + IDR_STATUSBAR_BATTERY_CHARGING_3, + IDR_STATUSBAR_BATTERY_CHARGING_4, + IDR_STATUSBAR_BATTERY_CHARGING_5, + IDR_STATUSBAR_BATTERY_CHARGING_6, + IDR_STATUSBAR_BATTERY_CHARGING_7, + IDR_STATUSBAR_BATTERY_CHARGING_8, + IDR_STATUSBAR_BATTERY_CHARGING_9, + IDR_STATUSBAR_BATTERY_CHARGING_10, + IDR_STATUSBAR_BATTERY_CHARGING_11, + IDR_STATUSBAR_BATTERY_CHARGING_12, + }; + static const int kDischargingImages[kNumPowerImages] = { + IDR_STATUSBAR_BATTERY_DISCHARGING_1, + IDR_STATUSBAR_BATTERY_DISCHARGING_2, + IDR_STATUSBAR_BATTERY_DISCHARGING_3, + IDR_STATUSBAR_BATTERY_DISCHARGING_4, + IDR_STATUSBAR_BATTERY_DISCHARGING_5, + IDR_STATUSBAR_BATTERY_DISCHARGING_6, + IDR_STATUSBAR_BATTERY_DISCHARGING_7, + IDR_STATUSBAR_BATTERY_DISCHARGING_8, + IDR_STATUSBAR_BATTERY_DISCHARGING_9, + IDR_STATUSBAR_BATTERY_DISCHARGING_10, + IDR_STATUSBAR_BATTERY_DISCHARGING_11, + IDR_STATUSBAR_BATTERY_DISCHARGING_12, + }; + + int index = static_cast<int>(battery_percentage_ / 100.0 * + nextafter(static_cast<float>(kNumPowerImages), 0)); + index = std::max(std::min(index, kNumPowerImages - 1), 0); + icon_id_ = line_power_on_ ? + kChargingImages[index] : kDischargingImages[index]; } + SetIcon(*ResourceBundle::GetSharedInstance().GetBitmapNamed(icon_id_)); + SetTooltipText(UTF16ToWide(GetLabelAt(0))); SchedulePaint(); } diff --git a/chrome/browser/chromeos/status/power_menu_button.h b/chrome/browser/chromeos/status/power_menu_button.h index 413b744..7f27333 100644 --- a/chrome/browser/chromeos/status/power_menu_button.h +++ b/chrome/browser/chromeos/status/power_menu_button.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_POWER_MENU_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_POWER_MENU_BUTTON_H_ +#pragma once #include "app/menus/menu_model.h" #include "chrome/browser/chromeos/cros/power_library.h" @@ -11,6 +12,10 @@ #include "views/controls/menu/menu_2.h" #include "views/controls/menu/view_menu_delegate.h" +namespace base { +class TimeDelta; +} + class SkBitmap; namespace chromeos { @@ -63,18 +68,27 @@ class PowerMenuButton : public StatusAreaButton, // This method will draw the |icon| in the appropriate place on the |canvas|. void DrawPowerIcon(gfx::Canvas* canvas, SkBitmap icon); - // Update the power icon depending on the power status. - void UpdateIcon(); + // Update the power icon and menu label info depending on the power status. + void UpdateIconAndLabelInfo(); // The number of power images. static const int kNumPowerImages; - // The power menu. - views::Menu2 power_menu_; + // Stored data gathered from CrosLibrary::PowerLibrary. + bool battery_is_present_; + bool line_power_on_; + bool battery_fully_charged_; + double battery_percentage_; + base::TimeDelta battery_time_to_full_; + base::TimeDelta battery_time_to_empty_; // The currently showing icon bitmap id. int icon_id_; + // The power menu. This needs to be initialized last since it calls into + // GetLabelAt() during construction. + views::Menu2 power_menu_; + DISALLOW_COPY_AND_ASSIGN(PowerMenuButton); }; diff --git a/chrome/browser/chromeos/status/power_menu_button_browsertest.cc b/chrome/browser/chromeos/status/power_menu_button_browsertest.cc index 62ffab6..66042fd 100644 --- a/chrome/browser/chromeos/status/power_menu_button_browsertest.cc +++ b/chrome/browser/chromeos/status/power_menu_button_browsertest.cc @@ -22,11 +22,16 @@ using ::testing::_; class PowerMenuButtonTest : public CrosInProcessBrowserTest { protected: - PowerMenuButtonTest() : CrosInProcessBrowserTest() {} + MockPowerLibrary *mock_power_library_; + + PowerMenuButtonTest() : CrosInProcessBrowserTest(), + mock_power_library_(NULL) { + } virtual void SetUpInProcessBrowserTestFixture() { - InitStatusAreaMocks(); - SetStatusAreaMocksExpectations(); + cros_mock_->InitStatusAreaMocks(); + cros_mock_->SetStatusAreaMocksExpectations(); + mock_power_library_ = cros_mock_->mock_power_library(); } PowerMenuButton* GetPowerMenuButton() { @@ -45,27 +50,70 @@ class PowerMenuButtonTest : public CrosInProcessBrowserTest { IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryMissingTest) { EXPECT_CALL(*mock_power_library_, battery_is_present()) - .WillRepeatedly((Return(false))); + .WillOnce((Return(false))) // no battery + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_percentage()) + .WillOnce((Return(42.0))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_fully_charged()) + .WillOnce((Return(false))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, line_power_on()) + .WillOnce((Return(false))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_empty()) + .WillOnce((Return(base::TimeDelta::FromMinutes(42)))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_full()) + .WillOnce((Return(base::TimeDelta::FromMinutes(24)))) + .RetiresOnSaturation(); EXPECT_EQ(IDR_STATUSBAR_BATTERY_MISSING, CallPowerChangedAndGetIconId()); } IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargedTest) { EXPECT_CALL(*mock_power_library_, battery_is_present()) - .WillRepeatedly((Return(true))); + .WillOnce((Return(true))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_percentage()) + .WillOnce((Return(42.0))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, battery_fully_charged()) - .WillRepeatedly((Return(true))); + .WillOnce((Return(true))) // fully charged + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, line_power_on()) - .WillRepeatedly((Return(true))); + .WillOnce((Return(true))) // plugged in + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_empty()) + .WillOnce((Return(base::TimeDelta::FromMinutes(42)))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_full()) + .WillOnce((Return(base::TimeDelta::FromMinutes(0)))) + .RetiresOnSaturation(); EXPECT_EQ(IDR_STATUSBAR_BATTERY_CHARGED, CallPowerChangedAndGetIconId()); } IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { + const int NUM_TIMES = 12; // 6 + 8*12 = 102 EXPECT_CALL(*mock_power_library_, battery_is_present()) - .WillRepeatedly((Return(true))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(true))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, battery_fully_charged()) - .WillRepeatedly((Return(false))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, line_power_on()) - .WillRepeatedly((Return(true))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(true))) // plugged in + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_empty()) + .Times(NUM_TIMES) + .WillRepeatedly((Return(base::TimeDelta::FromMinutes(42)))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_full()) + .Times(NUM_TIMES) + .WillRepeatedly((Return(base::TimeDelta::FromMinutes(24)))) + .RetiresOnSaturation(); // Test the 12 battery charging states. // NOTE: Use an array rather than just calculating a resource number to avoid @@ -87,7 +135,8 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { size_t id = 0; for (float percent = 6.0; percent < 100.0; percent += 8.0) { EXPECT_CALL(*mock_power_library_, battery_percentage()) - .WillRepeatedly((Return(percent))); + .WillOnce((Return(percent))) + .RetiresOnSaturation(); ASSERT_LT(id, arraysize(kChargingImages)); EXPECT_EQ(kChargingImages[id], CallPowerChangedAndGetIconId()); id++; @@ -95,12 +144,27 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryChargingTest) { } IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryDischargingTest) { + const int NUM_TIMES = 12; // 6 + 8*12 = 102 EXPECT_CALL(*mock_power_library_, battery_is_present()) - .WillRepeatedly((Return(true))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(true))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, battery_fully_charged()) - .WillRepeatedly((Return(false))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); EXPECT_CALL(*mock_power_library_, line_power_on()) - .WillRepeatedly((Return(false))); + .Times(NUM_TIMES) + .WillRepeatedly((Return(false))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_empty()) + .Times(NUM_TIMES) + .WillRepeatedly((Return(base::TimeDelta::FromMinutes(42)))) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_power_library_, battery_time_to_full()) + .Times(NUM_TIMES) + .WillRepeatedly((Return(base::TimeDelta::FromMinutes(24)))) + .RetiresOnSaturation(); // Test the 12 battery discharing states. // NOTE: Use an array rather than just calculating a resource number to avoid @@ -122,7 +186,8 @@ IN_PROC_BROWSER_TEST_F(PowerMenuButtonTest, BatteryDischargingTest) { size_t id = 0; for (float percent = 6.0; percent < 100.0; percent += 8.0) { EXPECT_CALL(*mock_power_library_, battery_percentage()) - .WillRepeatedly((Return(percent))); + .WillOnce((Return(percent))) + .RetiresOnSaturation(); ASSERT_LT(id, arraysize(kDischargingImages)); EXPECT_EQ(kDischargingImages[id], CallPowerChangedAndGetIconId()); id++; diff --git a/chrome/browser/chromeos/status/status_area_button.cc b/chrome/browser/chromeos/status/status_area_button.cc index 13aaad2..d2c34e6 100644 --- a/chrome/browser/chromeos/status/status_area_button.cc +++ b/chrome/browser/chromeos/status/status_area_button.cc @@ -1,10 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/status/status_area_button.h" -#include "app/resource_bundle.h" #include "gfx/canvas.h" #include "gfx/skbitmap_operations.h" #include "grit/theme_resources.h" @@ -17,29 +16,70 @@ namespace chromeos { // StatusAreaButton StatusAreaButton::StatusAreaButton(views::ViewMenuDelegate* menu_delegate) - : MenuButton(NULL, std::wstring(), menu_delegate, false) { + : MenuButton(NULL, std::wstring(), menu_delegate, false), + use_menu_button_paint_(false) { set_border(NULL); - SetShowHighlighted(true); + + // Use an offset that is top aligned with toolbar. + set_menu_offset(0, 2); } void StatusAreaButton::Paint(gfx::Canvas* canvas, bool for_drag) { if (state() == BS_PUSHED) { - DrawPressed(canvas); + // Apply 10% white when pushed down. + canvas->FillRectInt(SkColorSetARGB(0x19, 0xFF, 0xFF, 0xFF), + 0, 0, width(), height()); + } + + if (use_menu_button_paint_) { + views::MenuButton::Paint(canvas, for_drag); + } else { + if (state() == BS_PUSHED) + DrawPressed(canvas); + + DrawIcon(canvas); + PaintFocusBorder(canvas); } - DrawIcon(canvas); - PaintFocusBorder(canvas); } gfx::Size StatusAreaButton::GetPreferredSize() { // icons are 24x24 static const int kIconWidth = 24; static const int kIconHeight = 24; - gfx::Insets insets = GetInsets(); + gfx::Insets insets = views::MenuButton::GetInsets(); gfx::Size prefsize(kIconWidth + insets.width(), kIconHeight + insets.height()); + + // Adjusts size when use menu button paint. + if (use_menu_button_paint_) { + gfx::Size menu_button_size = views::MenuButton::GetPreferredSize(); + prefsize.SetSize( + std::max(prefsize.width(), menu_button_size.width()), + std::max(prefsize.height(), menu_button_size.height()) + ); + + // Shift 1-pixel down for odd number of pixels in vertical space. + if ((prefsize.height() - menu_button_size.height()) % 2) { + insets_.Set(insets.top() + 1, insets.left(), + insets.bottom(), insets.right()); + } + } + return prefsize; } +gfx::Insets StatusAreaButton::GetInsets() const { + return insets_; +} + +void StatusAreaButton::SetText(const std::wstring& text) { + // TextButtons normally remember the max text size, so the button's preferred + // size will always be as large as the largest text ever put in it. + // We clear that max text size, so we can adjust the size to fit the text. + ClearMaxTextSize(); + views::MenuButton::SetText(text); +} + void StatusAreaButton::DrawIcon(gfx::Canvas* canvas) { canvas->DrawBitmapInt(icon(), 0, 0); } diff --git a/chrome/browser/chromeos/status/status_area_button.h b/chrome/browser/chromeos/status/status_area_button.h index b751653..6d57178 100644 --- a/chrome/browser/chromeos/status/status_area_button.h +++ b/chrome/browser/chromeos/status/status_area_button.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_BUTTON_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_BUTTON_H_ +#pragma once #include "views/controls/button/menu_button.h" #include "views/controls/menu/view_menu_delegate.h" @@ -18,6 +19,15 @@ class StatusAreaButton : public views::MenuButton { virtual ~StatusAreaButton() {} virtual void Paint(gfx::Canvas* canvas, bool for_drag); virtual gfx::Size GetPreferredSize(); + virtual gfx::Insets GetInsets() const; + + // Overrides TextButton's SetText to clear max text size before seting new + // text content so that the button size would fit the new text size. + virtual void SetText(const std::wstring& text); + + void set_use_menu_button_paint(bool use_menu_button_paint) { + use_menu_button_paint_ = use_menu_button_paint; + } protected: // Draws the pressed icon. This is called before DrawIcon if the state is @@ -30,6 +40,12 @@ class StatusAreaButton : public views::MenuButton { // Otherwise, just call SetIcon() and the it will be handled for you. virtual void DrawIcon(gfx::Canvas* canvas); + // True if the button wants to use views::MenuButton drawings. + bool use_menu_button_paint_; + + // Insets to use for this button. + gfx::Insets insets_; + DISALLOW_COPY_AND_ASSIGN(StatusAreaButton); }; diff --git a/chrome/browser/chromeos/status/status_area_host.h b/chrome/browser/chromeos/status/status_area_host.h index e61cf7c..7e86e67 100644 --- a/chrome/browser/chromeos/status/status_area_host.h +++ b/chrome/browser/chromeos/status/status_area_host.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_HOST_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_HOST_H_ +#pragma once #include "gfx/native_widget_types.h" diff --git a/chrome/browser/chromeos/status/status_area_view.cc b/chrome/browser/chromeos/status/status_area_view.cc index 4f08452..27a3a54 100644 --- a/chrome/browser/chromeos/status/status_area_view.cc +++ b/chrome/browser/chromeos/status/status_area_view.cc @@ -19,9 +19,6 @@ namespace chromeos { // Number of pixels to separate each icon. const int kSeparation = 6; -// BrowserWindowGtk tiles its image with this offset -const int kCustomFrameBackgroundVerticalOffset = 15; - StatusAreaView::StatusAreaView(StatusAreaHost* host) : host_(host), clock_view_(NULL), diff --git a/chrome/browser/chromeos/status/status_area_view.h b/chrome/browser/chromeos/status/status_area_view.h index d30378b..0e4ecc3 100644 --- a/chrome/browser/chromeos/status/status_area_view.h +++ b/chrome/browser/chromeos/status/status_area_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_VIEW_H_ +#pragma once #include "base/basictypes.h" #include "chrome/browser/views/accessible_toolbar_view.h" diff --git a/chrome/browser/chromeos/system_key_event_listener.h b/chrome/browser/chromeos/system_key_event_listener.h index 117a3a8..b731b94 100644 --- a/chrome/browser/chromeos/system_key_event_listener.h +++ b/chrome/browser/chromeos/system_key_event_listener.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_KEY_EVENT_LISTENER_H_ #define CHROME_BROWSER_CHROMEOS_SYSTEM_KEY_EVENT_LISTENER_H_ +#pragma once #include "base/singleton.h" #include "chrome/browser/chromeos/wm_message_listener.h" diff --git a/chrome/browser/chromeos/tab_closeable_state_watcher.cc b/chrome/browser/chromeos/tab_closeable_state_watcher.cc index d5a01b0..7b7f2cb 100644 --- a/chrome/browser/chromeos/tab_closeable_state_watcher.cc +++ b/chrome/browser/chromeos/tab_closeable_state_watcher.cc @@ -4,10 +4,14 @@ #include "chrome/browser/chromeos/tab_closeable_state_watcher.h" +#include "base/command_line.h" +#include "chrome/browser/browser_shutdown.h" #include "chrome/browser/defaults.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" @@ -63,7 +67,9 @@ void TabCloseableStateWatcher::TabStripWatcher::TabChangedAt( TabCloseableStateWatcher::TabCloseableStateWatcher() : can_close_tab_(true), - signing_off_(false) { + signing_off_(false), + bwsi_session_( + CommandLine::ForCurrentProcess()->HasSwitch(switches::kBWSI)) { BrowserList::AddObserver(this); notification_registrar_.Add(this, NotificationType::APP_EXITING, NotificationService::AllSources()); @@ -71,7 +77,8 @@ TabCloseableStateWatcher::TabCloseableStateWatcher() TabCloseableStateWatcher::~TabCloseableStateWatcher() { BrowserList::RemoveObserver(this); - DCHECK(tabstrip_watchers_.empty()); + if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers()) + DCHECK(tabstrip_watchers_.empty()); } bool TabCloseableStateWatcher::CanCloseTab(const Browser* browser) const { @@ -183,7 +190,7 @@ void TabCloseableStateWatcher::CheckAndUpdateState( } else { // There's only 1 normal browser. if (!browser_to_check) browser_to_check = tabstrip_watchers_[0]->browser(); - if (browser_to_check->profile()->IsOffTheRecord()) { + if (browser_to_check->profile()->IsOffTheRecord() && !bwsi_session_) { new_can_close = true; } else { TabStripModel* tabstrip_model = browser_to_check->tabstrip_model(); @@ -232,8 +239,8 @@ bool TabCloseableStateWatcher::CanCloseBrowserImpl(const Browser* browser, return true; // If last normal browser is incognito, open a non-incognito window, - // and allow closing of incognito one. - if (browser->profile()->IsOffTheRecord()) { + // and allow closing of incognito one (if not BWSI). + if (browser->profile()->IsOffTheRecord() && !bwsi_session_) { *action_type = OPEN_WINDOW; return true; } diff --git a/chrome/browser/chromeos/tab_closeable_state_watcher.h b/chrome/browser/chromeos/tab_closeable_state_watcher.h index 5b786f2..9da71f2 100644 --- a/chrome/browser/chromeos/tab_closeable_state_watcher.h +++ b/chrome/browser/chromeos/tab_closeable_state_watcher.h @@ -4,12 +4,13 @@ #ifndef CHROME_BROWSER_CHROMEOS_TAB_CLOSEABLE_STATE_WATCHER_H_ #define CHROME_BROWSER_CHROMEOS_TAB_CLOSEABLE_STATE_WATCHER_H_ +#pragma once #include <vector> #include "chrome/browser/browser_list.h" #include "chrome/browser/tab_closeable_state_watcher.h" -#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/tabs/tab_strip_model_observer.h" #include "chrome/common/notification_registrar.h" namespace chromeos { @@ -17,6 +18,8 @@ namespace chromeos { // This class overrides ::TabCloseableStateWatcher to allow or disallow tabs or // browsers to be closed based on increase or decrease in number of tabs or // browsers. We only do this on Chromeos and only for non-tests. +// +// Normal session: // 1) A tab, and hence its containing browser, is not closeable if the tab is // the last NewTabPage in the last normal non-incognito browser and user is not // signing off. @@ -26,6 +29,15 @@ namespace chromeos { // 3) Or, if user closes a normal incognito browser or the last tab in it, the // browser closes, a new non-incognito normal browser is opened with a // NewTabPage (which, by rule 1, will not be closeable). +// +// BWSI session (all browsers are incognito): +// Almost the same as in the normal session, but +// 1) A tab, and hence its containing browser, is not closeable if the tab is +// the last NewTabPage in the last browser (again, all browsers are incognito +// browsers). +// 2-3) Otherwise, if user closes a normal incognito browser or the last tab in +// it, the browser stays open, the existing tabs are closed, and a new +// NewTabPage is open. class TabCloseableStateWatcher : public ::TabCloseableStateWatcher, public BrowserList::Observer, @@ -86,6 +98,9 @@ class TabCloseableStateWatcher : public ::TabCloseableStateWatcher, // allow closing of all tabs and browsers in this situation. bool signing_off_; + // In BWSI session? + bool bwsi_session_; + NotificationRegistrar notification_registrar_; // TabStripWatcher is a TabStripModelObserver that funnels all interesting diff --git a/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc b/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc index 3d01ab1..ea49119 100644 --- a/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc +++ b/chrome/browser/chromeos/tab_closeable_state_watcher_browsertest.cc @@ -1,16 +1,18 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/tab_closeable_state_watcher.h" #include "base/file_path.h" -#include "base/logging.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" +#include "chrome/browser/native_app_modal_dialog.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/common/url_constants.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" @@ -36,7 +38,8 @@ class TabCloseableStateWatcherTest : public InProcessBrowserTest { // Wrapper for Browser::AddTabWithURL void AddTabWithURL(Browser* browser, const GURL& url) { browser->AddTabWithURL(url, GURL(), PageTransition::TYPED, 0, - TabStripModel::ADD_SELECTED, NULL, std::string()); + TabStripModel::ADD_SELECTED, NULL, std::string(), + &browser); // Wait for page to finish loading. ui_test_utils::WaitForNavigation( &browser->GetSelectedTabContents()->controller()); @@ -280,7 +283,7 @@ IN_PROC_BROWSER_TEST_F(TabCloseableStateWatcherTest, TabContents* tab_contents = browser()->GetSelectedTabContents(); browser()->CloseWindow(); AppModalDialog* confirm = ui_test_utils::WaitForAppModalDialog(); - confirm->CancelWindow(); + confirm->native_dialog()->CancelAppModalDialog(); ui_test_utils::RunAllPendingInMessageLoop(); EXPECT_EQ(1u, BrowserList::size()); EXPECT_EQ(browser(), *(BrowserList::begin())); @@ -290,7 +293,7 @@ IN_PROC_BROWSER_TEST_F(TabCloseableStateWatcherTest, // Close the browser. browser()->CloseWindow(); confirm = ui_test_utils::WaitForAppModalDialog(); - confirm->AcceptWindow(); + confirm->native_dialog()->AcceptAppModalDialog(); ui_test_utils::RunAllPendingInMessageLoop(); } @@ -306,7 +309,7 @@ IN_PROC_BROWSER_TEST_F(TabCloseableStateWatcherTest, // Close browser, click OK in BeforeUnload confirm dialog. browser()->CloseWindow(); AppModalDialog* confirm = ui_test_utils::WaitForAppModalDialog(); - confirm->AcceptWindow(); + confirm->native_dialog()->AcceptAppModalDialog(); NewTabObserver new_tab_observer(browser()); EXPECT_EQ(1u, BrowserList::size()); EXPECT_EQ(browser(), *(BrowserList::begin())); @@ -315,4 +318,3 @@ IN_PROC_BROWSER_TEST_F(TabCloseableStateWatcherTest, } } // namespace chromeos - diff --git a/chrome/browser/chromeos/testdata/services_manifest.json b/chrome/browser/chromeos/testdata/services_manifest.json deleted file mode 100644 index 5efb835..0000000 --- a/chrome/browser/chromeos/testdata/services_manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Required. - "version": "1.0", - "app_menu" : { - "section_title" : "A partner application menu section title.", - "web_apps" : [ - "http://localhost/a/1", - "http://localhost/a/2", - ], - "support_page": "http://localhost/h", - "extensions": [ - "http://localhost/e/1", - "http://localhost/e/2", - ], - }, - - // Optional. - "initial_start_page": "http://localhost", -} diff --git a/chrome/browser/chromeos/testdata/startup_manifest.json b/chrome/browser/chromeos/testdata/startup_manifest.json deleted file mode 100644 index 25521a2..0000000 --- a/chrome/browser/chromeos/testdata/startup_manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // Required. - "version": "1.0", - "product_sku" : "SKU", - - // Optional. - "initial_locale" : "ru", - "initial_timezone" : "Asia/Tokyo", - "background_color" : "#880088", - "registration_url" : "http://www.google.com", - "setup_content" : [ - { - "content_locale" : "en_US", - "help_page" : "setup_content/en_US/help.html", - "eula_page" : "setup_content/en_US/eula.html", - }, - { - "content_locale" : "ru", - "help_page" : "setup_content/ru/help.html", - "eula_page" : "setup_content/ru/eula.html", - }, - ] -} diff --git a/chrome/browser/chromeos/update_browsertest.cc b/chrome/browser/chromeos/update_browsertest.cc index 9dca0bb..bbb67c3 100644 --- a/chrome/browser/chromeos/update_browsertest.cc +++ b/chrome/browser/chromeos/update_browsertest.cc @@ -4,13 +4,13 @@ #include "base/ref_counted.h" #include "base/utf_string_conversions.h" -#include "chrome/test/automation/dom_element_proxy.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" -#include "chrome/browser/chromeos/update_observer.h" #include "chrome/browser/chromeos/cros/mock_update_library.h" +#include "chrome/browser/chromeos/update_observer.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/url_constants.h" +#include "chrome/test/automation/dom_element_proxy.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" @@ -34,7 +34,7 @@ void CallObservers(chromeos::MockUpdateLibrary* lib, .WillRepeatedly((ReturnRef(x))) .RetiresOnSaturation(); FOR_EACH_OBSERVER(chromeos::UpdateLibrary::Observer, *observers, - Changed(lib)); + UpdateStatusChanged(lib)); } void FireSuccessSequence(chromeos::MockUpdateLibrary* lib, diff --git a/chrome/browser/chromeos/update_observer.cc b/chrome/browser/chromeos/update_observer.cc index ac1af56..60bd1e8 100644 --- a/chrome/browser/chromeos/update_observer.cc +++ b/chrome/browser/chromeos/update_observer.cc @@ -5,7 +5,7 @@ #include "chrome/browser/chromeos/update_observer.h" #include "app/l10n_util.h" -#include "base/string_util.h" +#include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/common/time_format.h" #include "grit/generated_resources.h" @@ -15,52 +15,22 @@ namespace chromeos { UpdateObserver::UpdateObserver(Profile* profile) : notification_(profile, "update.chromeos", IDR_NOTIFICATION_UPDATE, - l10n_util::GetStringUTF16(IDS_UPDATE_TITLE)), - progress_(-1) {} + l10n_util::GetStringUTF16(IDS_UPDATE_TITLE)) {} UpdateObserver::~UpdateObserver() { notification_.Hide(); } -void UpdateObserver::Changed(UpdateLibrary* object) { - switch (object->status().status) { - case UPDATE_STATUS_ERROR: - notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_ERROR), true); - break; - case UPDATE_STATUS_IDLE: - case UPDATE_STATUS_CHECKING_FOR_UPDATE: - // Do nothing in these cases, we don't want to notify the user of the - // check unless there is an update. We don't hide here because - // we want the final state to be sticky. - break; - case UPDATE_STATUS_UPDATE_AVAILABLE: - notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_AVAILABLE), - false); - break; - case UPDATE_STATUS_DOWNLOADING: - { - int progress = static_cast<int>(object->status().download_progress * - 100.0); - if (progress != progress_) { - progress_ = progress; - notification_.Show(l10n_util::GetStringFUTF16(IDS_UPDATE_DOWNLOADING, - IntToString16(progress_)), false); - } - } - break; - case UPDATE_STATUS_VERIFYING: - notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_VERIFYING), - false); - break; - case UPDATE_STATUS_FINALIZING: - notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_FINALIZING), - false); - break; - case UPDATE_STATUS_UPDATED_NEED_REBOOT: - notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_COMPLETED), true); - break; +void UpdateObserver::UpdateStatusChanged(UpdateLibrary* library) { +#if 0 + // TODO seanparent@chromium.org : This update should only be shown when an + // update is critical and should include a restart button using the + // update_engine restart API. Currently removed entirely per Kan's request. + + if (library->status().status == UPDATE_STATUS_UPDATED_NEED_REBOOT) { + notification_.Show(l10n_util::GetStringUTF16(IDS_UPDATE_COMPLETED), true); } +#endif } } // namespace chromeos - diff --git a/chrome/browser/chromeos/update_observer.h b/chrome/browser/chromeos/update_observer.h index e199958..3ec372b 100644 --- a/chrome/browser/chromeos/update_observer.h +++ b/chrome/browser/chromeos/update_observer.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_UPDATE_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_UPDATE_OBSERVER_H_ +#pragma once #include "base/basictypes.h" #include "base/time.h" @@ -23,10 +24,9 @@ class UpdateObserver : public UpdateLibrary::Observer { virtual ~UpdateObserver(); private: - virtual void Changed(UpdateLibrary* object); + virtual void UpdateStatusChanged(UpdateLibrary* library); SystemNotification notification_; - int progress_; // Last displayed remaining time in minutes DISALLOW_COPY_AND_ASSIGN(UpdateObserver); }; diff --git a/chrome/browser/chromeos/usb_mount_observer.cc b/chrome/browser/chromeos/usb_mount_observer.cc index d0e329f..fbfb501 100644 --- a/chrome/browser/chromeos/usb_mount_observer.cc +++ b/chrome/browser/chromeos/usb_mount_observer.cc @@ -8,8 +8,10 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/dom_ui/filebrowse_ui.h" -#include "chrome/browser/pref_service.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" namespace chromeos { @@ -52,7 +54,11 @@ void USBMountObserver::OpenFileBrowse(const std::string& url, bool small) { Browser* browser; Profile* profile; - profile = BrowserList::GetLastActive()->profile(); + browser = BrowserList::GetLastActive(); + if (browser == NULL) { + return; + } + profile = browser->profile(); PrefService* pref_service = profile->GetPrefs(); if (!pref_service->GetBoolean(prefs::kLabsAdvancedFilesystemEnabled)) { return; diff --git a/chrome/browser/chromeos/usb_mount_observer.h b/chrome/browser/chromeos/usb_mount_observer.h index 1dd78d3..5f30b31 100644 --- a/chrome/browser/chromeos/usb_mount_observer.h +++ b/chrome/browser/chromeos/usb_mount_observer.h @@ -4,15 +4,16 @@ #ifndef CHROME_BROWSER_CHROMEOS_USB_MOUNT_OBSERVER_H_ #define CHROME_BROWSER_CHROMEOS_USB_MOUNT_OBSERVER_H_ +#pragma once #include <string> #include <vector> #include "chrome/browser/chromeos/cros/mount_library.h" #include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" -#include "chrome/common/notification_registrar.h" class Browser; class Profile; diff --git a/chrome/browser/chromeos/usb_mount_observer_browsertest.cc b/chrome/browser/chromeos/usb_mount_observer_browsertest.cc index 3621067..e6a5c99 100644 --- a/chrome/browser/chromeos/usb_mount_observer_browsertest.cc +++ b/chrome/browser/chromeos/usb_mount_observer_browsertest.cc @@ -4,7 +4,6 @@ #include "base/ref_counted.h" #include "base/utf_string_conversions.h" -#include "chrome/test/automation/dom_element_proxy.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/chromeos/cros/mock_mount_library.h" @@ -12,6 +11,7 @@ #include "chrome/browser/dom_ui/mediaplayer_ui.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/url_constants.h" +#include "chrome/test/automation/dom_element_proxy.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" diff --git a/chrome/browser/chromeos/version_loader.cc b/chrome/browser/chromeos/version_loader.cc index 9475205..e24b357 100644 --- a/chrome/browser/chromeos/version_loader.cc +++ b/chrome/browser/chromeos/version_loader.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/message_loop.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" diff --git a/chrome/browser/chromeos/version_loader.h b/chrome/browser/chromeos/version_loader.h index c7072b5..0e5acb5 100644 --- a/chrome/browser/chromeos/version_loader.h +++ b/chrome/browser/chromeos/version_loader.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_VERSION_LOADER_H_ #define CHROME_BROWSER_CHROMEOS_VERSION_LOADER_H_ +#pragma once #include <string> diff --git a/chrome/browser/chromeos/view_ids.h b/chrome/browser/chromeos/view_ids.h index 8f0e016..f8b2794 100644 --- a/chrome/browser/chromeos/view_ids.h +++ b/chrome/browser/chromeos/view_ids.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_ #define CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_ +#pragma once #include "chrome/browser/view_ids.h" @@ -12,7 +13,6 @@ enum ChromeOSViewIds { // Start with the offset that is big enough to avoid possible // collison. VIEW_ID_STATUS_AREA = VIEW_ID_PREDEFINED_COUNT + 10000, - VIEW_ID_OTR_AVATAR, }; #endif // CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_ diff --git a/chrome/browser/chromeos/volume_bubble.cc b/chrome/browser/chromeos/volume_bubble.cc index 9b7543a..6c71fb3 100644 --- a/chrome/browser/chromeos/volume_bubble.cc +++ b/chrome/browser/chromeos/volume_bubble.cc @@ -17,7 +17,7 @@ const int kBubbleShowTimeoutSec = 2; const int kAnimationDurationMs = 200; // Horizontal relative position: 0 - leftmost, 0.5 - center, 1 - rightmost. -const double kVolumeBubbleXRatio = 0.18; +const double kVolumeBubbleXRatio = 0.5; // Vertical gap from the bottom of the screen in pixels. const int kVolumeBubbleBottomGap = 30; diff --git a/chrome/browser/chromeos/volume_bubble.h b/chrome/browser/chromeos/volume_bubble.h index 90bb034..c2da38e 100644 --- a/chrome/browser/chromeos/volume_bubble.h +++ b/chrome/browser/chromeos/volume_bubble.h @@ -4,8 +4,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_VOLUME_BUBBLE_H_ #define CHROME_BROWSER_CHROMEOS_VOLUME_BUBBLE_H_ +#pragma once -#include "app/active_window_watcher_x.h" #include "app/slide_animation.h" #include "base/singleton.h" #include "chrome/browser/views/info_bubble.h" diff --git a/chrome/browser/chromeos/volume_bubble_view.cc b/chrome/browser/chromeos/volume_bubble_view.cc index 5aa99c1..7bb500f 100644 --- a/chrome/browser/chromeos/volume_bubble_view.cc +++ b/chrome/browser/chromeos/volume_bubble_view.cc @@ -6,8 +6,8 @@ #include <string> -#include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/logging.h" #include "gfx/canvas.h" #include "grit/theme_resources.h" #include "third_party/skia/include/core/SkBitmap.h" diff --git a/chrome/browser/chromeos/volume_bubble_view.h b/chrome/browser/chromeos/volume_bubble_view.h index fbf77d2..f9ec0b0 100644 --- a/chrome/browser/chromeos/volume_bubble_view.h +++ b/chrome/browser/chromeos/volume_bubble_view.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_VOLUME_BUBBLE_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_VOLUME_BUBBLE_VIEW_H_ +#pragma once #include "views/view.h" diff --git a/chrome/browser/chromeos/wm_ipc.cc b/chrome/browser/chromeos/wm_ipc.cc index 1bb9a52..1dd0af5 100644 --- a/chrome/browser/chromeos/wm_ipc.cc +++ b/chrome/browser/chromeos/wm_ipc.cc @@ -11,8 +11,8 @@ extern "C" { #include "app/x11_util.h" #include "base/logging.h" -#include "base/singleton.h" #include "base/scoped_ptr.h" +#include "base/singleton.h" namespace chromeos { diff --git a/chrome/browser/chromeos/wm_ipc.h b/chrome/browser/chromeos/wm_ipc.h index 6161749..92fef71 100644 --- a/chrome/browser/chromeos/wm_ipc.h +++ b/chrome/browser/chromeos/wm_ipc.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_IPC_H_ #define CHROME_BROWSER_CHROMEOS_WM_IPC_H_ +#pragma once #include <gtk/gtk.h> #include <map> diff --git a/chrome/browser/chromeos/wm_message_listener.h b/chrome/browser/chromeos/wm_message_listener.h index 5972889..f389c4c 100644 --- a/chrome/browser/chromeos/wm_message_listener.h +++ b/chrome/browser/chromeos/wm_message_listener.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_MESSAGE_LISTENER_H_ #define CHROME_BROWSER_CHROMEOS_WM_MESSAGE_LISTENER_H_ +#pragma once #include <gtk/gtk.h> diff --git a/chrome/browser/chromeos/wm_overview_controller.cc b/chrome/browser/chromeos/wm_overview_controller.cc index 5e40520..8fbd74f 100644 --- a/chrome/browser/chromeos/wm_overview_controller.cc +++ b/chrome/browser/chromeos/wm_overview_controller.cc @@ -20,6 +20,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/tab_contents/thumbnail_generator.h" +#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/views/frame/browser_view.h" #include "chrome/common/notification_service.h" #include "views/widget/root_view.h" diff --git a/chrome/browser/chromeos/wm_overview_controller.h b/chrome/browser/chromeos/wm_overview_controller.h index 19a4ed0..c007b72 100644 --- a/chrome/browser/chromeos/wm_overview_controller.h +++ b/chrome/browser/chromeos/wm_overview_controller.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_ +#pragma once #include <vector> diff --git a/chrome/browser/chromeos/wm_overview_fav_icon.cc b/chrome/browser/chromeos/wm_overview_fav_icon.cc index 0568932..dea1b0e 100644 --- a/chrome/browser/chromeos/wm_overview_fav_icon.cc +++ b/chrome/browser/chromeos/wm_overview_fav_icon.cc @@ -9,8 +9,8 @@ #include "app/x11_util.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/chromeos/wm_overview_snapshot.h" -#include "skia/ext/image_operations.h" #include "cros/chromeos_wm_ipc_enums.h" +#include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "views/controls/image_view.h" #include "views/controls/label.h" diff --git a/chrome/browser/chromeos/wm_overview_fav_icon.h b/chrome/browser/chromeos/wm_overview_fav_icon.h index 0d22498..d4b4427 100644 --- a/chrome/browser/chromeos/wm_overview_fav_icon.h +++ b/chrome/browser/chromeos/wm_overview_fav_icon.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_FAV_ICON_H_ #define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_FAV_ICON_H_ +#pragma once #include "views/widget/widget_gtk.h" diff --git a/chrome/browser/chromeos/wm_overview_snapshot.h b/chrome/browser/chromeos/wm_overview_snapshot.h index 1257866..d6eeee0 100644 --- a/chrome/browser/chromeos/wm_overview_snapshot.h +++ b/chrome/browser/chromeos/wm_overview_snapshot.h @@ -4,11 +4,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_ #define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_ +#pragma once #include "third_party/skia/include/core/SkBitmap.h" +#include "views/controls/image_view.h" #include "views/view.h" #include "views/widget/widget_gtk.h" -#include "views/controls/image_view.h" class Browser; diff --git a/chrome/browser/chromeos/wm_overview_title.cc b/chrome/browser/chromeos/wm_overview_title.cc index 2c21bc6..0325065 100644 --- a/chrome/browser/chromeos/wm_overview_title.cc +++ b/chrome/browser/chromeos/wm_overview_title.cc @@ -8,6 +8,7 @@ #include "app/x11_util.h" #include "base/string16.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/chromeos/drop_shadow_label.h" @@ -42,9 +43,9 @@ namespace { // given value (well, unless the font at size 1 is taller than the // given value). Font FindFontThisHigh(int pixels, Font base) { - Font font = Font::CreateFont(base.FontName(), 1); + Font font(base.GetFontName(), 1); Font last_font = font; - while (font.height() < pixels) { + while (font.GetHeight() < pixels) { last_font = font; font = font.DeriveFont(1, Font::BOLD); } diff --git a/chrome/browser/chromeos/wm_overview_title.h b/chrome/browser/chromeos/wm_overview_title.h index 90e4bd5..b145432 100644 --- a/chrome/browser/chromeos/wm_overview_title.h +++ b/chrome/browser/chromeos/wm_overview_title.h @@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_TITLE_H_ #define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_TITLE_H_ +#pragma once #include "base/string16.h" #include "views/widget/widget_gtk.h" |