wizard_controller.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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/wizard_controller.h" 6 7#include <gdk/gdk.h> 8#include <signal.h> 9#include <sys/types.h> 10 11#include <string> 12#include <vector> 13 14#include "base/command_line.h" 15#include "base/file_util.h" 16#include "base/logging.h" 17#include "base/threading/thread_restrictions.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/chromeos/cros/cros_library.h" 20#include "chrome/browser/chromeos/cros/cryptohome_library.h" 21#include "chrome/browser/chromeos/cros/input_method_library.h" 22#include "chrome/browser/chromeos/cros/login_library.h" 23#include "chrome/browser/chromeos/cros/system_library.h" 24#include "chrome/browser/chromeos/customization_document.h" 25#include "chrome/browser/chromeos/input_method/input_method_util.h" 26#include "chrome/browser/chromeos/language_preferences.h" 27#include "chrome/browser/chromeos/login/account_screen.h" 28#include "chrome/browser/chromeos/login/apply_services_customization.h" 29#include "chrome/browser/chromeos/login/background_view.h" 30#include "chrome/browser/chromeos/login/eula_view.h" 31#include "chrome/browser/chromeos/login/existing_user_controller.h" 32#include "chrome/browser/chromeos/login/helper.h" 33#include "chrome/browser/chromeos/login/html_page_screen.h" 34#include "chrome/browser/chromeos/login/language_switch_menu.h" 35#include "chrome/browser/chromeos/login/login_utils.h" 36#include "chrome/browser/chromeos/login/network_screen.h" 37#include "chrome/browser/chromeos/login/registration_screen.h" 38#include "chrome/browser/chromeos/login/rounded_rect_painter.h" 39#include "chrome/browser/chromeos/login/update_screen.h" 40#include "chrome/browser/chromeos/login/user_image_screen.h" 41#include "chrome/browser/chromeos/login/user_manager.h" 42#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" 43#include "chrome/browser/chromeos/wm_ipc.h" 44#include "chrome/browser/prefs/pref_service.h" 45#include "chrome/browser/profiles/profile_manager.h" 46#include "chrome/common/chrome_switches.h" 47#include "chrome/common/pref_names.h" 48#include "content/common/notification_service.h" 49#include "content/common/notification_type.h" 50#include "third_party/cros/chromeos_wm_ipc_enums.h" 51#include "ui/base/resource/resource_bundle.h" 52#include "unicode/timezone.h" 53#include "views/accelerator.h" 54#include "views/painter.h" 55#include "views/view.h" 56#include "views/widget/widget_gtk.h" 57 58namespace { 59 60// A boolean pref of the EULA accepted flag. 61const char kEulaAccepted[] = "EulaAccepted"; 62 63// A boolean pref of the OOBE complete flag (first OOBE part before login). 64const char kOobeComplete[] = "OobeComplete"; 65 66// A boolean pref of the device registered flag (second part after first login). 67const char kDeviceRegistered[] = "DeviceRegistered"; 68 69// Path to OEM partner startup customization manifest. 70const char kStartupCustomizationManifestPath[] = 71 "/opt/oem/etc/startup_manifest.json"; 72 73// Path to flag file indicating that both parts of OOBE were completed. 74const char kOobeCompleteFlagFilePath[] = "/home/chronos/.oobe_completed"; 75 76// Time in seconds that we wait for the device to reboot. 77// If reboot didn't happen, ask user to reboot device manually. 78const int kWaitForRebootTimeSec = 3; 79 80// Interval in ms which is used for smooth screen showing. 81static int kShowDelayMs = 400; 82 83// RootView of the Widget WizardController creates. Contains the contents of the 84// WizardController. 85class ContentView : public views::View { 86 public: 87 ContentView() 88 : accel_toggle_accessibility_( 89 chromeos::WizardAccessibilityHelper::GetAccelerator()) { 90#if defined(OFFICIAL_BUILD) 91 accel_cancel_update_ = views::Accelerator(ui::VKEY_ESCAPE, 92 true, true, true); 93#else 94 accel_cancel_update_ = views::Accelerator(ui::VKEY_ESCAPE, 95 false, false, false); 96 accel_account_screen_ = views::Accelerator(ui::VKEY_A, 97 false, true, true); 98 accel_login_screen_ = views::Accelerator(ui::VKEY_L, 99 false, true, true); 100 accel_network_screen_ = views::Accelerator(ui::VKEY_N, 101 false, true, true); 102 accel_update_screen_ = views::Accelerator(ui::VKEY_U, 103 false, true, true); 104 accel_image_screen_ = views::Accelerator(ui::VKEY_I, 105 false, true, true); 106 accel_eula_screen_ = views::Accelerator(ui::VKEY_E, 107 false, true, true); 108 accel_register_screen_ = views::Accelerator(ui::VKEY_R, 109 false, true, true); 110 AddAccelerator(accel_account_screen_); 111 AddAccelerator(accel_login_screen_); 112 AddAccelerator(accel_network_screen_); 113 AddAccelerator(accel_update_screen_); 114 AddAccelerator(accel_image_screen_); 115 AddAccelerator(accel_eula_screen_); 116 AddAccelerator(accel_register_screen_); 117#endif 118 AddAccelerator(accel_toggle_accessibility_); 119 AddAccelerator(accel_cancel_update_); 120 } 121 122 ~ContentView() { 123 NotificationService::current()->Notify( 124 NotificationType::WIZARD_CONTENT_VIEW_DESTROYED, 125 NotificationService::AllSources(), 126 NotificationService::NoDetails()); 127 } 128 129 bool AcceleratorPressed(const views::Accelerator& accel) { 130 WizardController* controller = WizardController::default_controller(); 131 if (!controller) 132 return false; 133 134 if (accel == accel_toggle_accessibility_) { 135 chromeos::WizardAccessibilityHelper::GetInstance()->ToggleAccessibility(); 136 } else if (accel == accel_cancel_update_) { 137 controller->CancelOOBEUpdate(); 138#if !defined(OFFICIAL_BUILD) 139 } else if (accel == accel_account_screen_) { 140 controller->ShowAccountScreen(); 141 } else if (accel == accel_login_screen_) { 142 controller->ShowLoginScreen(); 143 } else if (accel == accel_network_screen_) { 144 controller->ShowNetworkScreen(); 145 } else if (accel == accel_update_screen_) { 146 controller->ShowUpdateScreen(); 147 } else if (accel == accel_image_screen_) { 148 controller->ShowUserImageScreen(); 149 } else if (accel == accel_eula_screen_) { 150 controller->ShowEulaScreen(); 151 } else if (accel == accel_register_screen_) { 152 controller->ShowRegistrationScreen(); 153#endif 154 } else { 155 return false; 156 } 157 158 return true; 159 } 160 161 virtual void Layout() { 162 for (int i = 0; i < child_count(); ++i) { 163 views::View* cur = GetChildViewAt(i); 164 if (cur->IsVisible()) 165 cur->SetBounds(0, 0, width(), height()); 166 } 167 } 168 169 private: 170 scoped_ptr<views::Painter> painter_; 171 172#if !defined(OFFICIAL_BUILD) 173 views::Accelerator accel_account_screen_; 174 views::Accelerator accel_login_screen_; 175 views::Accelerator accel_network_screen_; 176 views::Accelerator accel_update_screen_; 177 views::Accelerator accel_image_screen_; 178 views::Accelerator accel_eula_screen_; 179 views::Accelerator accel_register_screen_; 180#endif 181 views::Accelerator accel_toggle_accessibility_; 182 views::Accelerator accel_cancel_update_; 183 184 DISALLOW_COPY_AND_ASSIGN(ContentView); 185}; 186 187void DeleteWizardControllerAndLaunchBrowser(WizardController* controller) { 188 delete controller; 189 // Launch browser after controller is deleted and its windows are closed. 190 chromeos::LoginUtils::Get()->EnableBrowserLaunch(true); 191 BrowserThread::PostTask( 192 BrowserThread::UI, 193 FROM_HERE, 194 NewRunnableFunction(&chromeos::LoginUtils::DoBrowserLaunch, 195 ProfileManager::GetDefaultProfile())); 196} 197 198const chromeos::StartupCustomizationDocument* LoadStartupManifest() { 199 // Loading manifest causes us to do blocking IO on UI thread. 200 // Temporarily allow it until we fix http://crosbug.com/11103 201 base::ThreadRestrictions::ScopedAllowIO allow_io; 202 FilePath startup_manifest_path(kStartupCustomizationManifestPath); 203 if (file_util::PathExists(startup_manifest_path)) { 204 scoped_ptr<chromeos::StartupCustomizationDocument> customization( 205 new chromeos::StartupCustomizationDocument()); 206 bool manifest_loaded = customization->LoadManifestFromFile( 207 startup_manifest_path); 208 if (manifest_loaded) { 209 VLOG(1) << "Startup manifest loaded successfully"; 210 return customization.release(); 211 } 212 LOG(ERROR) << "Error loading startup manifest: " 213 << kStartupCustomizationManifestPath; 214 } 215 216 return NULL; 217} 218 219// Returns true if startup manifest defines valid registration URL. 220bool IsRegistrationScreenValid( 221 const chromeos::StartupCustomizationDocument* startup_manifest) { 222 return startup_manifest != NULL && 223 GURL(startup_manifest->registration_url()).is_valid(); 224} 225 226// Saves boolean "Local State" preference and forces its persistence to disk. 227void SaveBoolPreferenceForced(const char* pref_name, bool value) { 228 PrefService* prefs = g_browser_process->local_state(); 229 prefs->SetBoolean(pref_name, value); 230 prefs->SavePersistentPrefs(); 231} 232 233// Saves integer "Local State" preference and forces its persistence to disk. 234void SaveIntegerPreferenceForced(const char* pref_name, int value) { 235 PrefService* prefs = g_browser_process->local_state(); 236 prefs->SetInteger(pref_name, value); 237 prefs->SavePersistentPrefs(); 238} 239 240// Saves the hardware keyboard to "Locale State" from the locale code. 241// The information will be used in input_method::GetHardwareInputMethodId(). 242void SaveHardwareKeyboardFromLocaleCode(const std::string& locale) { 243 std::vector<std::string> input_method_ids; 244 if (chromeos::input_method::GetInputMethodIdsFromLanguageCode( 245 locale, 246 chromeos::input_method::kKeyboardLayoutsOnly, 247 &input_method_ids)) { 248 // The output list |input_method_ids| is sorted by popularity, hence 249 // input_method_ids[0] now contains the most popular keyboard layout 250 // for the given locale. 251 PrefService* prefs = g_browser_process->local_state(); 252 prefs->SetString(prefs::kHardwareKeyboardLayout, input_method_ids[0]); 253 // This asks the file thread to save the prefs (i.e. doesn't block). 254 // The latest values of Local State reside in memory so we can safely 255 // get the value of kHardwareKeyboardLayout even if the data is not 256 // yet saved to disk. 257 prefs->SavePersistentPrefs(); 258 } 259} 260 261} // namespace 262 263const char WizardController::kNetworkScreenName[] = "network"; 264const char WizardController::kLoginScreenName[] = "login"; 265const char WizardController::kAccountScreenName[] = "account"; 266const char WizardController::kUpdateScreenName[] = "update"; 267const char WizardController::kUserImageScreenName[] = "image"; 268const char WizardController::kEulaScreenName[] = "eula"; 269const char WizardController::kRegistrationScreenName[] = "register"; 270const char WizardController::kHTMLPageScreenName[] = "html"; 271 272// Passing this parameter as a "first screen" initiates full OOBE flow. 273const char WizardController::kOutOfBoxScreenName[] = "oobe"; 274 275// Special test value that commands not to create any window yet. 276const char WizardController::kTestNoScreenName[] = "test:nowindow"; 277 278// Initialize default controller. 279// static 280WizardController* WizardController::default_controller_ = NULL; 281 282/////////////////////////////////////////////////////////////////////////////// 283// WizardController, public: 284 285WizardController::WizardController() 286 : widget_(NULL), 287 background_widget_(NULL), 288 background_view_(NULL), 289 contents_(NULL), 290 current_screen_(NULL), 291 initial_show_(true), 292#if defined(OFFICIAL_BUILD) 293 is_official_build_(true), 294#else 295 is_official_build_(false), 296#endif 297 is_out_of_box_(false), 298 observer_(NULL) { 299 DCHECK(default_controller_ == NULL); 300 default_controller_ = this; 301 registrar_.Add( 302 this, 303 NotificationType::APP_TERMINATING, 304 NotificationService::AllSources()); 305} 306 307WizardController::~WizardController() { 308 // Close ends up deleting the widget. 309 if (background_widget_) 310 background_widget_->Close(); 311 312 if (widget_) 313 widget_->Close(); 314 315 default_controller_ = NULL; 316 chromeos::WizardAccessibilityHelper::GetInstance()-> 317 UnregisterNotifications(); 318} 319 320void WizardController::Init(const std::string& first_screen_name, 321 const gfx::Rect& screen_bounds) { 322 VLOG(1) << "Starting OOBE wizard with screen: " << first_screen_name; 323 DCHECK(!contents_); 324 first_screen_name_ = first_screen_name; 325 326 // When device is not registered yet we need to load startup manifest as well. 327 // In case of OOBE (network-EULA-update) manifest has been loaded in 328 // ShowLoginWizard(). 329 if (IsOobeCompleted() && !IsDeviceRegistered()) 330 SetCustomization(LoadStartupManifest()); 331 332 screen_bounds_ = screen_bounds; 333 contents_ = new ContentView(); 334 335 bool oobe_complete = IsOobeCompleted(); 336 if (!oobe_complete || first_screen_name == kOutOfBoxScreenName) { 337 is_out_of_box_ = true; 338 } 339 340 ShowFirstScreen(first_screen_name); 341} 342 343void WizardController::ShowBackground(const gfx::Rect& bounds) { 344 DCHECK(!background_widget_); 345 background_widget_ = 346 chromeos::BackgroundView::CreateWindowContainingView(bounds, 347 GURL(), 348 &background_view_); 349 background_view_->SetOobeProgressBarVisible(true); 350 background_widget_->Show(); 351} 352 353void WizardController::OwnBackground( 354 views::Widget* background_widget, 355 chromeos::BackgroundView* background_view) { 356 DCHECK(!background_widget_); 357 background_widget_ = background_widget; 358 background_view_ = background_view; 359} 360 361void WizardController::CancelOOBEUpdate() { 362 if (update_screen_.get() && 363 update_screen_.get() == current_screen_) { 364 GetUpdateScreen()->CancelUpdate(); 365 } 366} 367 368chromeos::NetworkScreen* WizardController::GetNetworkScreen() { 369 if (!network_screen_.get()) 370 network_screen_.reset(new chromeos::NetworkScreen(this)); 371 return network_screen_.get(); 372} 373 374chromeos::AccountScreen* WizardController::GetAccountScreen() { 375 if (!account_screen_.get()) 376 account_screen_.reset(new chromeos::AccountScreen(this)); 377 return account_screen_.get(); 378} 379 380chromeos::UpdateScreen* WizardController::GetUpdateScreen() { 381 if (!update_screen_.get()) { 382 update_screen_.reset(new chromeos::UpdateScreen(this)); 383 update_screen_->SetRebootCheckDelay(kWaitForRebootTimeSec); 384 } 385 return update_screen_.get(); 386} 387 388chromeos::UserImageScreen* WizardController::GetUserImageScreen() { 389 if (!user_image_screen_.get()) 390 user_image_screen_.reset(new chromeos::UserImageScreen(this)); 391 return user_image_screen_.get(); 392} 393 394chromeos::EulaScreen* WizardController::GetEulaScreen() { 395 if (!eula_screen_.get()) 396 eula_screen_.reset(new chromeos::EulaScreen(this)); 397 return eula_screen_.get(); 398} 399 400chromeos::RegistrationScreen* WizardController::GetRegistrationScreen() { 401 if (!registration_screen_.get()) 402 registration_screen_.reset(new chromeos::RegistrationScreen(this)); 403 return registration_screen_.get(); 404} 405 406chromeos::HTMLPageScreen* WizardController::GetHTMLPageScreen() { 407 if (!html_page_screen_.get()) { 408 CommandLine* command_line = CommandLine::ForCurrentProcess(); 409 std::string url; 410 // It's strange but args may contains empty strings. 411 for (size_t i = 0; i < command_line->args().size(); i++) { 412 if (!command_line->args()[i].empty()) { 413 DCHECK(url.empty()) << "More than one URL in command line"; 414 url = command_line->args()[i]; 415 } 416 } 417 DCHECK(!url.empty()) << "No URL in commane line"; 418 html_page_screen_.reset(new chromeos::HTMLPageScreen(this, url)); 419 } 420 return html_page_screen_.get(); 421} 422 423void WizardController::ShowNetworkScreen() { 424 SetStatusAreaVisible(false); 425 SetCurrentScreen(GetNetworkScreen()); 426 background_view_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK); 427} 428 429chromeos::ExistingUserController* WizardController::ShowLoginScreen() { 430 SetStatusAreaVisible(true); 431 background_view_->SetOobeProgress(chromeos::BackgroundView::SIGNIN); 432 433 // Initiate services customization. 434 chromeos::ApplyServicesCustomization::StartIfNeeded(); 435 436 std::vector<chromeos::UserManager::User> users; 437 if (chromeos::CrosLibrary::Get()->EnsureLoaded()) 438 users = chromeos::UserManager::Get()->GetUsers(); 439 440 // ExistingUserController deletes itself. 441 gfx::Rect screen_bounds = background_widget_->GetWindowScreenBounds(); 442 chromeos::ExistingUserController* controller = 443 new chromeos::ExistingUserController(screen_bounds); 444 controller->OwnBackground(background_widget_, background_view_); 445 controller->Init(users); 446 background_widget_ = NULL; 447 background_view_ = NULL; 448 449 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 450 451 return controller; 452} 453 454void WizardController::ShowAccountScreen() { 455 VLOG(1) << "Showing create account screen."; 456 SetStatusAreaVisible(true); 457 SetCurrentScreen(GetAccountScreen()); 458} 459 460void WizardController::ShowUpdateScreen() { 461 VLOG(1) << "Showing update screen."; 462 SetStatusAreaVisible(true); 463 SetCurrentScreen(GetUpdateScreen()); 464 // There is no special step for update. 465#if defined(OFFICIAL_BUILD) 466 background_view_->SetOobeProgress(chromeos::BackgroundView::EULA); 467#else 468 background_view_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK); 469#endif 470} 471 472void WizardController::ShowUserImageScreen() { 473 VLOG(1) << "Showing user image screen."; 474 SetStatusAreaVisible(false); 475 SetCurrentScreen(GetUserImageScreen()); 476 background_view_->SetOobeProgress(chromeos::BackgroundView::PICTURE); 477 background_view_->EnableShutdownButton(false); 478} 479 480void WizardController::ShowEulaScreen() { 481 VLOG(1) << "Showing EULA screen."; 482 SetStatusAreaVisible(false); 483 SetCurrentScreen(GetEulaScreen()); 484#if defined(OFFICIAL_BUILD) 485 background_view_->SetOobeProgress(chromeos::BackgroundView::EULA); 486#endif 487} 488 489void WizardController::ShowRegistrationScreen() { 490 if (!IsRegistrationScreenValid(GetCustomization())) { 491 VLOG(1) << "Skipping registration screen: manifest not defined or invalid " 492 "URL."; 493 OnRegistrationSkipped(); 494 return; 495 } 496 VLOG(1) << "Showing registration screen."; 497 SetStatusAreaVisible(true); 498 SetCurrentScreen(GetRegistrationScreen()); 499#if defined(OFFICIAL_BUILD) 500 background_view_->SetOobeProgress(chromeos::BackgroundView::REGISTRATION); 501#endif 502} 503 504void WizardController::ShowHTMLPageScreen() { 505 VLOG(1) << "Showing HTML page screen."; 506 SetStatusAreaVisible(true); 507 background_view_->SetOobeProgressBarVisible(false); 508 SetCurrentScreen(GetHTMLPageScreen()); 509} 510 511void WizardController::SetCustomization( 512 const chromeos::StartupCustomizationDocument* customization) { 513 customization_.reset(customization); 514} 515 516const chromeos::StartupCustomizationDocument* 517 WizardController::GetCustomization() const { 518 return customization_.get(); 519} 520 521void WizardController::SkipRegistration() { 522 if (current_screen_ == GetRegistrationScreen()) 523 OnRegistrationSkipped(); 524 else 525 LOG(ERROR) << "Registration screen is not active."; 526} 527 528void WizardController::Observe(NotificationType type, 529 const NotificationSource& source, 530 const NotificationDetails& details) { 531 CHECK(type == NotificationType::APP_TERMINATING); 532 533 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 534 MessageLoop::current()->Quit(); 535 registrar_.Remove(this, 536 NotificationType::APP_TERMINATING, 537 NotificationService::AllSources()); 538} 539 540// static 541void WizardController::RegisterPrefs(PrefService* local_state) { 542 local_state->RegisterBooleanPref(kOobeComplete, false); 543 local_state->RegisterIntegerPref(kDeviceRegistered, -1); 544 local_state->RegisterBooleanPref(kEulaAccepted, false); 545 // Check if the pref is already registered in case 546 // Preferences::RegisterUserPrefs runs before this code in the future. 547 if (local_state->FindPreference(prefs::kAccessibilityEnabled) == NULL) { 548 local_state->RegisterBooleanPref(prefs::kAccessibilityEnabled, false); 549 } 550} 551 552/////////////////////////////////////////////////////////////////////////////// 553// WizardController, ExitHandlers: 554void WizardController::OnNetworkConnected() { 555 if (is_official_build_) { 556 if (!IsEulaAccepted()) { 557 ShowEulaScreen(); 558 } else { 559 // Possible cases: 560 // 1. EULA was accepted, forced shutdown/reboot during update. 561 // 2. EULA was accepted, planned reboot after update. 562 // Make sure that device is up-to-date. 563 InitiateOOBEUpdate(); 564 } 565 } else { 566 InitiateOOBEUpdate(); 567 } 568} 569 570void WizardController::OnNetworkOffline() { 571 // TODO(dpolukhin): if(is_out_of_box_) we cannot work offline and 572 // should report some error message here and stay on the same screen. 573 ShowLoginScreen(); 574} 575 576void WizardController::OnAccountCreateBack() { 577 ShowLoginScreen(); 578} 579 580void WizardController::OnAccountCreated() { 581 chromeos::ExistingUserController* controller = ShowLoginScreen(); 582 DCHECK(controller); 583 controller->Login(username_, password_); 584 // TODO(dpolukhin): clear password memory for real. Now it is not 585 // a problem because we can't extract password from the form. 586 password_.clear(); 587} 588 589void WizardController::OnConnectionFailed() { 590 // TODO(dpolukhin): show error message after login screen is displayed. 591 ShowLoginScreen(); 592} 593 594void WizardController::OnUpdateCompleted() { 595 OnOOBECompleted(); 596} 597 598void WizardController::OnEulaAccepted() { 599 MarkEulaAccepted(); 600 if (chromeos::CrosLibrary::Get()->EnsureLoaded()) { 601 // TPM password could be seen on EULA screen, now it's safe to clear it. 602 chromeos::CrosLibrary::Get()-> 603 GetCryptohomeLibrary()->TpmClearStoredPassword(); 604 } 605 InitiateOOBEUpdate(); 606} 607 608void WizardController::OnUpdateErrorCheckingForUpdate() { 609 // TODO(nkostylev): Update should be required during OOBE. 610 // We do not want to block users from being able to proceed to the login 611 // screen if there is any error checking for an update. 612 // They could use "browse without sign-in" feature to set up the network to be 613 // able to perform the update later. 614 OnOOBECompleted(); 615} 616 617void WizardController::OnUpdateErrorUpdating() { 618 // If there was an error while getting or applying the update, 619 // return to network selection screen. 620 // TODO(nkostylev): Show message to the user explaining update error. 621 // TODO(nkostylev): Update should be required during OOBE. 622 // Temporary fix, need to migrate to new API. http://crosbug.com/4321 623 OnOOBECompleted(); 624} 625 626void WizardController::OnUserImageSelected() { 627 // We're on the stack, so don't try and delete us now. 628 // We should launch browser only after we delete the controller and close 629 // its windows. 630 BrowserThread::PostTask( 631 BrowserThread::UI, 632 FROM_HERE, 633 NewRunnableFunction(&DeleteWizardControllerAndLaunchBrowser, 634 this)); 635 // TODO(avayvod): Sync image with Google Sync. 636} 637 638void WizardController::OnUserImageSkipped() { 639 OnUserImageSelected(); 640} 641 642void WizardController::OnRegistrationSuccess() { 643 MarkDeviceRegistered(); 644 if (chromeos::UserManager::Get()->IsLoggedInAsGuest()) { 645 chromeos::LoginUtils::Get()->CompleteOffTheRecordLogin(start_url_); 646 } else { 647 ShowUserImageScreen(); 648 } 649} 650 651void WizardController::OnRegistrationSkipped() { 652 // TODO(nkostylev): Track in a histogram? 653 OnRegistrationSuccess(); 654} 655 656void WizardController::OnOOBECompleted() { 657 MarkOobeCompleted(); 658 ShowLoginScreen(); 659} 660 661void WizardController::InitiateOOBEUpdate() { 662 GetUpdateScreen()->StartUpdate(); 663 SetCurrentScreenSmooth(GetUpdateScreen(), true); 664} 665 666/////////////////////////////////////////////////////////////////////////////// 667// WizardController, private: 668 669views::WidgetGtk* WizardController::CreateScreenWindow( 670 const gfx::Rect& bounds, bool initial_show) { 671 views::WidgetGtk* window = 672 new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); 673 widget_ = window; 674 window->MakeTransparent(); 675 // Window transparency makes background flicker through controls that 676 // are constantly updating its contents (like image view with video 677 // stream). Hence enabling double buffer. 678 window->EnableDoubleBuffer(true); 679 window->Init(NULL, bounds); 680 std::vector<int> params; 681 // For initial show WM would animate background window. 682 // Otherwise it stays unchaged. 683 params.push_back(initial_show); 684 chromeos::WmIpc::instance()->SetWindowType( 685 window->GetNativeView(), 686 chromeos::WM_IPC_WINDOW_LOGIN_GUEST, 687 ¶ms); 688 window->SetContentsView(contents_); 689 return window; 690} 691 692gfx::Rect WizardController::GetWizardScreenBounds(int screen_width, 693 int screen_height) const { 694 int offset_x = (screen_bounds_.width() - screen_width) / 2; 695 int offset_y = (screen_bounds_.height() - screen_height) / 2; 696 int window_x = screen_bounds_.x() + offset_x; 697 int window_y = screen_bounds_.y() + offset_y; 698 return gfx::Rect(window_x, window_y, screen_width, screen_height); 699} 700 701 702void WizardController::SetCurrentScreen(WizardScreen* new_current) { 703 SetCurrentScreenSmooth(new_current, false); 704} 705 706void WizardController::ShowCurrentScreen() { 707 smooth_show_timer_.Stop(); 708 709 bool force_widget_show = false; 710 views::WidgetGtk* window = NULL; 711 712 gfx::Rect current_bounds; 713 if (widget_) 714 current_bounds = widget_->GetClientAreaScreenBounds(); 715 gfx::Size new_screen_size = current_screen_->GetScreenSize(); 716 gfx::Rect new_bounds = GetWizardScreenBounds(new_screen_size.width(), 717 new_screen_size.height()); 718 if (new_bounds != current_bounds) { 719 if (widget_) 720 widget_->Close(); 721 force_widget_show = true; 722 window = CreateScreenWindow(new_bounds, initial_show_); 723 } 724 current_screen_->Show(); 725 contents_->Layout(); 726 contents_->SchedulePaint(); 727 if (force_widget_show) { 728 // This keeps the window from flashing at startup. 729 GdkWindow* gdk_window = window->GetNativeView()->window; 730 gdk_window_set_back_pixmap(gdk_window, NULL, false); 731 if (widget_) 732 widget_->Show(); 733 } 734} 735 736void WizardController::SetCurrentScreenSmooth(WizardScreen* new_current, 737 bool use_smoothing) { 738 if (current_screen_ == new_current || new_current == NULL) 739 return; 740 741 smooth_show_timer_.Stop(); 742 743 if (current_screen_) { 744 initial_show_ = false; 745 current_screen_->Hide(); 746 } 747 748 current_screen_ = new_current; 749 750 if (use_smoothing) { 751 smooth_show_timer_.Start( 752 base::TimeDelta::FromMilliseconds(kShowDelayMs), 753 this, 754 &WizardController::ShowCurrentScreen); 755 contents_->Layout(); 756 contents_->SchedulePaint(); 757 } else { 758 ShowCurrentScreen(); 759 } 760} 761 762void WizardController::SetStatusAreaVisible(bool visible) { 763 // When ExistingUserController passes background ownership 764 // to WizardController it happens after screen is shown. 765 if (background_view_) { 766 background_view_->SetStatusAreaVisible(visible); 767 } 768} 769 770void WizardController::ShowFirstScreen(const std::string& first_screen_name) { 771 if (first_screen_name == kNetworkScreenName) { 772 ShowNetworkScreen(); 773 } else if (first_screen_name == kLoginScreenName) { 774 ShowLoginScreen(); 775 } else if (first_screen_name == kAccountScreenName) { 776 ShowAccountScreen(); 777 } else if (first_screen_name == kUpdateScreenName) { 778 InitiateOOBEUpdate(); 779 } else if (first_screen_name == kUserImageScreenName) { 780 ShowUserImageScreen(); 781 } else if (first_screen_name == kEulaScreenName) { 782 ShowEulaScreen(); 783 } else if (first_screen_name == kRegistrationScreenName) { 784 if (is_official_build_) { 785 ShowRegistrationScreen(); 786 } else { 787 // Just proceed to image screen. 788 OnRegistrationSuccess(); 789 } 790 } else if (first_screen_name == kHTMLPageScreenName) { 791 ShowHTMLPageScreen(); 792 } else if (first_screen_name != kTestNoScreenName) { 793 if (is_out_of_box_) { 794 ShowNetworkScreen(); 795 } else { 796 ShowLoginScreen(); 797 } 798 } 799} 800 801// static 802bool WizardController::IsEulaAccepted() { 803 return g_browser_process->local_state()->GetBoolean(kEulaAccepted); 804} 805 806// static 807bool WizardController::IsOobeCompleted() { 808 return g_browser_process->local_state()->GetBoolean(kOobeComplete); 809} 810 811// static 812void WizardController::MarkEulaAccepted() { 813 SaveBoolPreferenceForced(kEulaAccepted, true); 814} 815 816// static 817void WizardController::MarkOobeCompleted() { 818 SaveBoolPreferenceForced(kOobeComplete, true); 819} 820 821static void CreateOobeCompleteFlagFile() { 822 // Create flag file for boot-time init scripts. 823 FilePath oobe_complete_path(kOobeCompleteFlagFilePath); 824 if (!file_util::PathExists(oobe_complete_path)) { 825 FILE* oobe_flag_file = file_util::OpenFile(oobe_complete_path, "w+b"); 826 if (oobe_flag_file == NULL) 827 DLOG(WARNING) << kOobeCompleteFlagFilePath << " doesn't exist."; 828 else 829 file_util::CloseFile(oobe_flag_file); 830 } 831} 832 833// static 834bool WizardController::IsDeviceRegistered() { 835 int value = g_browser_process->local_state()->GetInteger(kDeviceRegistered); 836 if (value > 0) { 837 // Recreate flag file in case it was lost. 838 BrowserThread::PostTask( 839 BrowserThread::FILE, 840 FROM_HERE, 841 NewRunnableFunction(&CreateOobeCompleteFlagFile)); 842 return true; 843 } else if (value == 0) { 844 return false; 845 } else { 846 // Pref is not set. For compatibility check flag file. It causes blocking 847 // IO on UI thread. But it's required for update from old versions. 848 base::ThreadRestrictions::ScopedAllowIO allow_io; 849 FilePath oobe_complete_flag_file_path(kOobeCompleteFlagFilePath); 850 bool file_exists = file_util::PathExists(oobe_complete_flag_file_path); 851 SaveIntegerPreferenceForced(kDeviceRegistered, file_exists ? 1 : 0); 852 return file_exists; 853 } 854} 855 856// static 857void WizardController::MarkDeviceRegistered() { 858 SaveIntegerPreferenceForced(kDeviceRegistered, 1); 859 BrowserThread::PostTask( 860 BrowserThread::FILE, 861 FROM_HERE, 862 NewRunnableFunction(&CreateOobeCompleteFlagFile)); 863} 864 865// static 866bool WizardController::IsRegisterScreenDefined() { 867 const chromeos::StartupCustomizationDocument* manifest = NULL; 868 // This method will be called from ExistingUserController too 869 // when Wizard instance doesn't exist. 870 if (default_controller()) 871 manifest = default_controller()->GetCustomization(); 872 else 873 manifest = LoadStartupManifest(); 874 return IsRegistrationScreenValid(manifest); 875} 876 877/////////////////////////////////////////////////////////////////////////////// 878// WizardController, chromeos::ScreenObserver overrides: 879void WizardController::OnExit(ExitCodes exit_code) { 880 LOG(INFO) << "Wizard screen exit code: " << exit_code; 881 switch (exit_code) { 882 case NETWORK_CONNECTED: 883 OnNetworkConnected(); 884 break; 885 case NETWORK_OFFLINE: 886 OnNetworkOffline(); 887 break; 888 case ACCOUNT_CREATE_BACK: 889 OnAccountCreateBack(); 890 break; 891 case ACCOUNT_CREATED: 892 OnAccountCreated(); 893 break; 894 case CONNECTION_FAILED: 895 OnConnectionFailed(); 896 break; 897 case UPDATE_INSTALLED: 898 case UPDATE_NOUPDATE: 899 OnUpdateCompleted(); 900 break; 901 case UPDATE_ERROR_CHECKING_FOR_UPDATE: 902 OnUpdateErrorCheckingForUpdate(); 903 break; 904 case UPDATE_ERROR_UPDATING: 905 OnUpdateErrorUpdating(); 906 break; 907 case USER_IMAGE_SELECTED: 908 OnUserImageSelected(); 909 break; 910 case USER_IMAGE_SKIPPED: 911 OnUserImageSkipped(); 912 break; 913 case EULA_ACCEPTED: 914 OnEulaAccepted(); 915 break; 916 case EULA_BACK: 917 ShowNetworkScreen(); 918 break; 919 case REGISTRATION_SUCCESS: 920 OnRegistrationSuccess(); 921 break; 922 case REGISTRATION_SKIPPED: 923 OnRegistrationSkipped(); 924 break; 925 default: 926 NOTREACHED(); 927 } 928} 929 930void WizardController::OnSetUserNamePassword(const std::string& username, 931 const std::string& password) { 932 username_ = username; 933 password_ = password; 934} 935 936/////////////////////////////////////////////////////////////////////////////// 937// WizardController, WizardScreen overrides: 938views::View* WizardController::GetWizardView() { 939 return contents_; 940} 941 942chromeos::ScreenObserver* WizardController::GetObserver(WizardScreen* screen) { 943 return observer_ ? observer_ : this; 944} 945 946void WizardController::SetZeroDelays() { 947 kShowDelayMs = 0; 948} 949 950namespace browser { 951 952// Declared in browser_dialogs.h so that others don't need to depend on our .h. 953void ShowLoginWizard(const std::string& first_screen_name, 954 const gfx::Size& size) { 955 VLOG(1) << "Showing login screen: " << first_screen_name; 956 957 // The login screen will enable alternate keyboard layouts, but we don't want 958 // to start the IME process unless one is selected. 959 chromeos::CrosLibrary::Get()->GetInputMethodLibrary()-> 960 SetDeferImeStartup(true); 961 // Tell the window manager that the user isn't logged in. 962 chromeos::WmIpc::instance()->SetLoggedInProperty(false); 963 964 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty 965 // and US dvorak keyboard layouts. 966 if (g_browser_process && g_browser_process->local_state()) { 967 const std::string locale = g_browser_process->GetApplicationLocale(); 968 // If the preferred keyboard for the login screen has been saved, use it. 969 const std::string initial_input_method_id = 970 g_browser_process->local_state()->GetString( 971 chromeos::language_prefs::kPreferredKeyboardLayout); 972 chromeos::input_method::EnableInputMethods( 973 locale, chromeos::input_method::kKeyboardLayoutsOnly, 974 initial_input_method_id); 975 } 976 977 gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(size)); 978 979 // Check whether we need to execute OOBE process. 980 bool oobe_complete = WizardController::IsOobeCompleted(); 981 bool show_login_screen = 982 (first_screen_name.empty() && oobe_complete) || 983 first_screen_name == WizardController::kLoginScreenName; 984 985 if (show_login_screen && chromeos::CrosLibrary::Get()->EnsureLoaded()) { 986 std::vector<chromeos::UserManager::User> users = 987 chromeos::UserManager::Get()->GetUsers(); 988 989 // Fix for users who updated device and thus never passed register screen. 990 // If we already have users, we assume that it is not a second part of 991 // OOBE. See http://crosbug.com/6289 992 if (!WizardController::IsDeviceRegistered() && !users.empty()) { 993 VLOG(1) << "Mark device registered because there are remembered users: " 994 << users.size(); 995 WizardController::MarkDeviceRegistered(); 996 } 997 998 // ExistingUserController deletes itself. 999 (new chromeos::ExistingUserController(screen_bounds))->Init(users); 1000 1001 // Initiate services customization. 1002 chromeos::ApplyServicesCustomization::StartIfNeeded(); 1003 1004 return; 1005 } 1006 1007 // Create and show the wizard. 1008 WizardController* controller = new WizardController(); 1009 1010 // Load startup manifest. 1011 controller->SetCustomization(LoadStartupManifest()); 1012 1013 std::string locale; 1014 if (controller->GetCustomization()) { 1015 // Switch to initial locale if specified by customization 1016 // and has not been set yet. We cannot call 1017 // chromeos::LanguageSwitchMenu::SwitchLanguage here before 1018 // EmitLoginPromptReady. 1019 const std::string current_locale = 1020 g_browser_process->local_state()->GetString(prefs::kApplicationLocale); 1021 VLOG(1) << "Current locale: " << current_locale; 1022 if (current_locale.empty()) { 1023 locale = controller->GetCustomization()->initial_locale(); 1024 VLOG(1) << "Initial locale: " << locale; 1025 if (!locale.empty()) { 1026 // Save the hardware keyboard from the locale code. 1027 SaveHardwareKeyboardFromLocaleCode(locale); 1028 // Then, enable the hardware keyboard. 1029 chromeos::input_method::EnableInputMethods( 1030 locale, 1031 chromeos::input_method::kKeyboardLayoutsOnly, 1032 chromeos::input_method::GetHardwareInputMethodId()); 1033 // Reloading resource bundle causes us to do blocking IO on UI thread. 1034 // Temporarily allow it until we fix http://crosbug.com/11102 1035 base::ThreadRestrictions::ScopedAllowIO allow_io; 1036 const std::string loaded_locale = 1037 ResourceBundle::ReloadSharedInstance(locale); 1038 CHECK(!loaded_locale.empty()) << "Locale could not be found for " 1039 << locale; 1040 // Set the application locale here so that the language switch 1041 // menu works properly with the newly loaded locale. 1042 g_browser_process->SetApplicationLocale(loaded_locale); 1043 } 1044 } 1045 } 1046 1047 controller->ShowBackground(screen_bounds); 1048 controller->Init(first_screen_name, screen_bounds); 1049 1050 chromeos::LoginUtils::Get()->PrewarmAuthentication(); 1051 if (chromeos::CrosLibrary::Get()->EnsureLoaded()) 1052 chromeos::CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady(); 1053 1054 if (controller->GetCustomization()) { 1055 // Set initial timezone if specified by customization. 1056 const std::string timezone_name = 1057 controller->GetCustomization()->initial_timezone(); 1058 VLOG(1) << "Initial time zone: " << timezone_name; 1059 // Apply locale customizations only once so preserve whatever locale 1060 // user has changed to during OOBE. 1061 if (!timezone_name.empty()) { 1062 icu::TimeZone* timezone = icu::TimeZone::createTimeZone( 1063 icu::UnicodeString::fromUTF8(timezone_name)); 1064 chromeos::CrosLibrary::Get()->GetSystemLibrary()->SetTimezone(timezone); 1065 } 1066 } 1067} 1068 1069} // namespace browser 1070