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      &params);
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