1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/login/base_login_display_host.h" 6 7 #include "base/file_util.h" 8 #include "base/logging.h" 9 #include "base/threading/thread_restrictions.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/chromeos/cros/cros_library.h" 12 #include "chrome/browser/chromeos/cros/input_method_library.h" 13 #include "chrome/browser/chromeos/cros/login_library.h" 14 #include "chrome/browser/chromeos/customization_document.h" 15 #include "chrome/browser/chromeos/input_method/input_method_util.h" 16 #include "chrome/browser/chromeos/language_preferences.h" 17 #include "chrome/browser/chromeos/login/existing_user_controller.h" 18 #include "chrome/browser/chromeos/login/helper.h" 19 #include "chrome/browser/chromeos/login/language_switch_menu.h" 20 #include "chrome/browser/chromeos/login/login_utils.h" 21 #include "chrome/browser/chromeos/login/user_manager.h" 22 #include "chrome/browser/chromeos/login/views_login_display_host.h" 23 #include "chrome/browser/chromeos/login/wizard_controller.h" 24 #include "chrome/browser/chromeos/system_access.h" 25 #include "chrome/browser/chromeos/wm_ipc.h" 26 #include "chrome/browser/prefs/pref_service.h" 27 #include "content/common/notification_service.h" 28 #include "content/common/notification_type.h" 29 #include "chrome/common/pref_names.h" 30 #include "googleurl/src/gurl.h" 31 #include "third_party/cros/chromeos_wm_ipc_enums.h" 32 #include "ui/base/resource/resource_bundle.h" 33 #include "unicode/timezone.h" 34 35 #if defined(TOUCH_UI) 36 #include "base/command_line.h" 37 #include "chrome/browser/chromeos/login/dom_login_display_host.h" 38 #endif 39 40 namespace { 41 42 // Determines the hardware keyboard from the given locale code 43 // and the OEM layout information, and saves it to "Locale State". 44 // The information will be used in input_method::GetHardwareInputMethodId(). 45 void DetermineAndSaveHardwareKeyboard(const std::string& locale, 46 const std::string& oem_layout) { 47 std::string layout; 48 if (!oem_layout.empty()) { 49 // If the OEM layout information is provided, use it. 50 layout = oem_layout; 51 } else { 52 // Otherwise, determine the hardware keyboard from the locale. 53 std::vector<std::string> input_method_ids; 54 if (chromeos::input_method::GetInputMethodIdsFromLanguageCode( 55 locale, 56 chromeos::input_method::kKeyboardLayoutsOnly, 57 &input_method_ids)) { 58 // The output list |input_method_ids| is sorted by popularity, hence 59 // input_method_ids[0] now contains the most popular keyboard layout 60 // for the given locale. 61 layout = input_method_ids[0]; 62 } 63 } 64 65 if (!layout.empty()) { 66 PrefService* prefs = g_browser_process->local_state(); 67 prefs->SetString(prefs::kHardwareKeyboardLayout, layout); 68 // This asks the file thread to save the prefs (i.e. doesn't block). 69 // The latest values of Local State reside in memory so we can safely 70 // get the value of kHardwareKeyboardLayout even if the data is not 71 // yet saved to disk. 72 prefs->SavePersistentPrefs(); 73 } 74 } 75 76 } // namespace 77 78 namespace chromeos { 79 80 // static 81 LoginDisplayHost* BaseLoginDisplayHost::default_host_ = NULL; 82 83 // BaseLoginDisplayHost -------------------------------------------------------- 84 85 BaseLoginDisplayHost::BaseLoginDisplayHost(const gfx::Rect& background_bounds) 86 : background_bounds_(background_bounds) { 87 registrar_.Add( 88 this, 89 NotificationType::APP_TERMINATING, 90 NotificationService::AllSources()); 91 DCHECK(default_host_ == NULL); 92 default_host_ = this; 93 } 94 95 BaseLoginDisplayHost::~BaseLoginDisplayHost() { 96 default_host_ = NULL; 97 } 98 99 // LoginDisplayHost implementation --------------------------------------------- 100 101 void BaseLoginDisplayHost::OnSessionStart() { 102 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 103 } 104 105 void BaseLoginDisplayHost::StartWizard( 106 const std::string& first_screen_name, 107 const GURL& start_url) { 108 DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name; 109 // Create and show the wizard. 110 wizard_controller_.reset(); // Only one controller in a time. 111 wizard_controller_.reset(new WizardController(this, background_bounds_)); 112 wizard_controller_->set_start_url(start_url); 113 ShowBackground(); 114 if (!WizardController::IsDeviceRegistered()) 115 SetOobeProgressBarVisible(true); 116 wizard_controller_->Init(first_screen_name); 117 } 118 119 void BaseLoginDisplayHost::StartSignInScreen() { 120 DVLOG(1) << "Starting sign in screen"; 121 std::vector<chromeos::UserManager::User> users = 122 chromeos::UserManager::Get()->GetUsers(); 123 124 // Fix for users who updated device and thus never passed register screen. 125 // If we already have users, we assume that it is not a second part of 126 // OOBE. See http://crosbug.com/6289 127 if (!WizardController::IsDeviceRegistered() && !users.empty()) { 128 VLOG(1) << "Mark device registered because there are remembered users: " 129 << users.size(); 130 WizardController::MarkDeviceRegistered(); 131 } 132 133 sign_in_controller_.reset(); // Only one controller in a time. 134 sign_in_controller_.reset(new chromeos::ExistingUserController(this)); 135 ShowBackground(); 136 SetShutdownButtonEnabled(true); 137 sign_in_controller_->Init(users); 138 139 // Initiate services customization manifest fetching. 140 ServicesCustomizationDocument::GetInstance()->StartFetching(); 141 } 142 143 // BaseLoginDisplayHost -------------------------------------------------------- 144 145 void BaseLoginDisplayHost::Observe(NotificationType type, 146 const NotificationSource& source, 147 const NotificationDetails& details) { 148 CHECK(type == NotificationType::APP_TERMINATING); 149 150 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 151 MessageLoop::current()->Quit(); 152 registrar_.Remove(this, 153 NotificationType::APP_TERMINATING, 154 NotificationService::AllSources()); 155 } 156 157 } // namespace chromeos 158 159 // browser::ShowLoginWizard implementation ------------------------------------- 160 161 namespace browser { 162 163 // Declared in browser_dialogs.h so that others don't need to depend on our .h. 164 // TODO(nkostylev): Split this into a smaller functions. 165 void ShowLoginWizard(const std::string& first_screen_name, 166 const gfx::Size& size) { 167 VLOG(1) << "Showing login screen: " << first_screen_name; 168 169 // The login screen will enable alternate keyboard layouts, but we don't want 170 // to start the IME process unless one is selected. 171 chromeos::CrosLibrary::Get()->GetInputMethodLibrary()-> 172 SetDeferImeStartup(true); 173 // Tell the window manager that the user isn't logged in. 174 chromeos::WmIpc::instance()->SetLoggedInProperty(false); 175 176 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty 177 // and US dvorak keyboard layouts. 178 if (g_browser_process && g_browser_process->local_state()) { 179 const std::string locale = g_browser_process->GetApplicationLocale(); 180 // If the preferred keyboard for the login screen has been saved, use it. 181 std::string initial_input_method_id = 182 g_browser_process->local_state()->GetString( 183 chromeos::language_prefs::kPreferredKeyboardLayout); 184 if (initial_input_method_id.empty()) { 185 // If kPreferredKeyboardLayout is not specified, use the hardware layout. 186 initial_input_method_id = 187 chromeos::input_method::GetHardwareInputMethodId(); 188 } 189 chromeos::input_method::EnableInputMethods( 190 locale, chromeos::input_method::kKeyboardLayoutsOnly, 191 initial_input_method_id); 192 } 193 194 gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(size)); 195 196 // Check whether we need to execute OOBE process. 197 bool oobe_complete = WizardController::IsOobeCompleted(); 198 bool show_login_screen = 199 (first_screen_name.empty() && oobe_complete) || 200 first_screen_name == WizardController::kLoginScreenName; 201 202 // TODO(nkostylev) Create LoginDisplayHost instance based on flag. 203 #if defined(TOUCH_UI) 204 chromeos::LoginDisplayHost* display_host; 205 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDOMLogin)) { 206 display_host = new chromeos::DOMLoginDisplayHost(screen_bounds); 207 } else { 208 display_host = new chromeos::ViewsLoginDisplayHost(screen_bounds); 209 } 210 #else 211 chromeos::LoginDisplayHost* display_host = 212 new chromeos::ViewsLoginDisplayHost(screen_bounds); 213 #endif 214 if (show_login_screen && chromeos::CrosLibrary::Get()->EnsureLoaded()) { 215 display_host->StartSignInScreen(); 216 return; 217 } 218 219 // Load startup manifest. 220 const chromeos::StartupCustomizationDocument* startup_manifest = 221 chromeos::StartupCustomizationDocument::GetInstance(); 222 223 std::string locale; 224 if (startup_manifest->IsReady()) { 225 // Switch to initial locale if specified by customization 226 // and has not been set yet. We cannot call 227 // chromeos::LanguageSwitchMenu::SwitchLanguage here before 228 // EmitLoginPromptReady. 229 PrefService* prefs = g_browser_process->local_state(); 230 const std::string current_locale = 231 prefs->GetString(prefs::kApplicationLocale); 232 VLOG(1) << "Current locale: " << current_locale; 233 if (current_locale.empty()) { 234 locale = startup_manifest->initial_locale(); 235 std::string layout = startup_manifest->keyboard_layout(); 236 VLOG(1) << "Initial locale: " << locale 237 << "keyboard layout " << layout; 238 if (!locale.empty()) { 239 // Save initial locale from VPD/customization manifest as current 240 // Chrome locale. Otherwise it will be lost if Chrome restarts. 241 // Don't need to schedule pref save because setting initial local 242 // will enforce preference saving. 243 prefs->SetString(prefs::kApplicationLocale, locale); 244 WizardController::SetInitialLocale(locale); 245 // Determine keyboard layout from OEM customization (if provided) or 246 // initial locale and save it in preferences. 247 DetermineAndSaveHardwareKeyboard(locale, layout); 248 // Then, enable the hardware keyboard. 249 chromeos::input_method::EnableInputMethods( 250 locale, 251 chromeos::input_method::kKeyboardLayoutsOnly, 252 chromeos::input_method::GetHardwareInputMethodId()); 253 // Reloading resource bundle causes us to do blocking IO on UI thread. 254 // Temporarily allow it until we fix http://crosbug.com/11102 255 base::ThreadRestrictions::ScopedAllowIO allow_io; 256 const std::string loaded_locale = 257 ResourceBundle::ReloadSharedInstance(locale); 258 CHECK(!loaded_locale.empty()) << "Locale could not be found for " 259 << locale; 260 // Set the application locale here so that the language switch 261 // menu works properly with the newly loaded locale. 262 g_browser_process->SetApplicationLocale(loaded_locale); 263 } 264 } 265 } 266 267 display_host->StartWizard(first_screen_name, GURL()); 268 269 chromeos::LoginUtils::Get()->PrewarmAuthentication(); 270 if (chromeos::CrosLibrary::Get()->EnsureLoaded()) 271 chromeos::CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady(); 272 273 if (startup_manifest->IsReady()) { 274 // Set initial timezone if specified by customization. 275 const std::string timezone_name = startup_manifest->initial_timezone(); 276 VLOG(1) << "Initial time zone: " << timezone_name; 277 // Apply locale customizations only once so preserve whatever locale 278 // user has changed to during OOBE. 279 if (!timezone_name.empty()) { 280 icu::TimeZone* timezone = icu::TimeZone::createTimeZone( 281 icu::UnicodeString::fromUTF8(timezone_name)); 282 CHECK(timezone) << "Timezone could not be set for " << timezone_name; 283 chromeos::SystemAccess::GetInstance()->SetTimezone(*timezone); 284 } 285 } 286 } 287 288 } // namespace browser 289