Home | History | Annotate | Download | only in login
      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/background_view.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/string16.h"
     11 #include "base/string_util.h"
     12 #include "base/stringprintf.h"
     13 #include "base/utf_string_conversions.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/chromeos/login/helper.h"
     16 #include "chrome/browser/chromeos/login/login_utils.h"
     17 #include "chrome/browser/chromeos/login/oobe_progress_bar.h"
     18 #include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
     19 #include "chrome/browser/chromeos/login/rounded_rect_painter.h"
     20 #include "chrome/browser/chromeos/login/shutdown_button.h"
     21 #include "chrome/browser/chromeos/login/wizard_controller.h"
     22 #include "chrome/browser/chromeos/status/clock_menu_button.h"
     23 #include "chrome/browser/chromeos/status/input_method_menu_button.h"
     24 #include "chrome/browser/chromeos/status/network_menu_button.h"
     25 #include "chrome/browser/chromeos/status/status_area_view.h"
     26 #include "chrome/browser/chromeos/wm_ipc.h"
     27 #include "chrome/browser/policy/browser_policy_connector.h"
     28 #include "chrome/browser/profiles/profile_manager.h"
     29 #include "chrome/browser/ui/views/dom_view.h"
     30 #include "chrome/browser/ui/views/window.h"
     31 #include "chrome/common/chrome_version_info.h"
     32 #include "googleurl/src/gurl.h"
     33 #include "grit/chromium_strings.h"
     34 #include "grit/generated_resources.h"
     35 #include "grit/theme_resources.h"
     36 #include "third_party/cros/chromeos_wm_ipc_enums.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 #include "ui/base/resource/resource_bundle.h"
     39 #include "ui/base/x/x11_util.h"
     40 #include "ui/gfx/gtk_util.h"
     41 #include "views/controls/button/text_button.h"
     42 #include "views/controls/label.h"
     43 #include "views/screen.h"
     44 #include "views/widget/widget_gtk.h"
     45 #include "views/window/window.h"
     46 
     47 // X Windows headers have "#define Status int". That interferes with
     48 // NetworkLibrary header which defines enum "Status".
     49 #include <X11/cursorfont.h>  // NOLINT
     50 #include <X11/Xcursor/Xcursor.h>  // NOLINT
     51 
     52 using views::Widget;
     53 using views::WidgetGtk;
     54 
     55 namespace {
     56 
     57 const SkColor kVersionColor = 0xff5c739f;
     58 const char kPlatformLabel[] = "cros:";
     59 
     60 // Returns the corresponding step id for step constant.
     61 int GetStepId(size_t step) {
     62   switch (step) {
     63     case chromeos::BackgroundView::SELECT_NETWORK:
     64       return IDS_OOBE_SELECT_NETWORK;
     65     case chromeos::BackgroundView::EULA:
     66       return IDS_OOBE_EULA;
     67     case chromeos::BackgroundView::SIGNIN:
     68       return IDS_OOBE_SIGNIN;
     69     case chromeos::BackgroundView::REGISTRATION:
     70       return IDS_OOBE_REGISTRATION;
     71     case chromeos::BackgroundView::PICTURE:
     72       return IDS_OOBE_PICTURE;
     73     default:
     74       NOTREACHED();
     75       return 0;
     76   }
     77 }
     78 
     79 // The same as TextButton but switches cursor to hand cursor when mouse
     80 // is over the button.
     81 class TextButtonWithHandCursorOver : public views::TextButton {
     82  public:
     83   TextButtonWithHandCursorOver(views::ButtonListener* listener,
     84                                const std::wstring& text)
     85       : views::TextButton(listener, text) {
     86   }
     87 
     88   virtual ~TextButtonWithHandCursorOver() {}
     89 
     90   virtual gfx::NativeCursor GetCursorForPoint(
     91       ui::EventType event_type,
     92       const gfx::Point& p) {
     93     if (!IsEnabled()) {
     94       return NULL;
     95     }
     96     return gfx::GetCursor(GDK_HAND2);
     97   }
     98 
     99  private:
    100   DISALLOW_COPY_AND_ASSIGN(TextButtonWithHandCursorOver);
    101 };
    102 
    103 // This gets rid of the ugly X default cursor.
    104 static void ResetXCursor() {
    105   // TODO(sky): nuke this once new window manager is in place.
    106   Display* display = ui::GetXDisplay();
    107   Cursor cursor = XCreateFontCursor(display, XC_left_ptr);
    108   XID root_window = ui::GetX11RootWindow();
    109   XSetWindowAttributes attr;
    110   attr.cursor = cursor;
    111   XChangeWindowAttributes(display, root_window, CWCursor, &attr);
    112 }
    113 
    114 }  // namespace
    115 
    116 namespace chromeos {
    117 
    118 ///////////////////////////////////////////////////////////////////////////////
    119 // BackgroundView public:
    120 
    121 BackgroundView::BackgroundView()
    122     : status_area_(NULL),
    123       os_version_label_(NULL),
    124       boot_times_label_(NULL),
    125       progress_bar_(NULL),
    126       shutdown_button_(NULL),
    127       did_paint_(false),
    128 #if defined(OFFICIAL_BUILD)
    129       is_official_build_(true),
    130 #else
    131       is_official_build_(false),
    132 #endif
    133       background_area_(NULL) {
    134 }
    135 
    136 void BackgroundView::Init(const GURL& background_url) {
    137   views::Painter* painter = CreateBackgroundPainter();
    138   set_background(views::Background::CreateBackgroundPainter(true, painter));
    139   InitStatusArea();
    140   InitInfoLabels();
    141   if (!background_url.is_empty()) {
    142     Profile* profile = ProfileManager::GetDefaultProfile();
    143     background_area_ = new DOMView();
    144     AddChildView(background_area_);
    145     background_area_->Init(profile, NULL);
    146     background_area_->SetVisible(false);
    147     background_area_->LoadURL(background_url);
    148   }
    149 }
    150 
    151 void BackgroundView::EnableShutdownButton(bool enable) {
    152   if (enable) {
    153     if (shutdown_button_)
    154       return;
    155     shutdown_button_ = new ShutdownButton();
    156     shutdown_button_->Init();
    157     AddChildView(shutdown_button_);
    158   } else {
    159     if (!shutdown_button_)
    160       return;
    161     delete shutdown_button_;
    162     shutdown_button_ = NULL;
    163     SchedulePaint();
    164   }
    165 }
    166 
    167 // static
    168 views::Widget* BackgroundView::CreateWindowContainingView(
    169     const gfx::Rect& bounds,
    170     const GURL& background_url,
    171     BackgroundView** view) {
    172   ResetXCursor();
    173 
    174   Widget* window = Widget::CreateWidget(
    175       Widget::CreateParams(Widget::CreateParams::TYPE_WINDOW));
    176   window->Init(NULL, bounds);
    177   *view = new BackgroundView();
    178   (*view)->Init(background_url);
    179 
    180   if ((*view)->ScreenSaverEnabled())
    181     (*view)->ShowScreenSaver();
    182 
    183   window->SetContentsView(*view);
    184 
    185   (*view)->UpdateWindowType();
    186 
    187   // This keeps the window from flashing at startup.
    188   GdkWindow* gdk_window = window->GetNativeView()->window;
    189   gdk_window_set_back_pixmap(gdk_window, NULL, false);
    190 
    191   LoginUtils::Get()->SetBackgroundView(*view);
    192 
    193   return window;
    194 }
    195 
    196 void BackgroundView::CreateModalPopup(views::WindowDelegate* view) {
    197   views::Window* window = browser::CreateViewsWindow(
    198       GetNativeWindow(), gfx::Rect(), view);
    199   window->SetIsAlwaysOnTop(true);
    200   window->Show();
    201 }
    202 
    203 gfx::NativeWindow BackgroundView::GetNativeWindow() const {
    204   return
    205       GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
    206 }
    207 
    208 void BackgroundView::SetStatusAreaVisible(bool visible) {
    209   status_area_->SetVisible(visible);
    210 }
    211 
    212 void BackgroundView::SetStatusAreaEnabled(bool enable) {
    213   status_area_->MakeButtonsActive(enable);
    214 }
    215 
    216 void BackgroundView::SetOobeProgressBarVisible(bool visible) {
    217   if (!progress_bar_ && visible)
    218     InitProgressBar();
    219 
    220   if (progress_bar_)
    221     progress_bar_->SetVisible(visible);
    222 }
    223 
    224 bool BackgroundView::IsOobeProgressBarVisible() {
    225   return progress_bar_ && progress_bar_->IsVisible();
    226 }
    227 
    228 void BackgroundView::SetOobeProgress(LoginStep step) {
    229   DCHECK(step < STEPS_COUNT);
    230   if (progress_bar_)
    231     progress_bar_->SetStep(GetStepId(step));
    232 }
    233 
    234 void BackgroundView::ShowScreenSaver() {
    235   SetStatusAreaVisible(false);
    236   background_area_->SetVisible(true);
    237 }
    238 
    239 void BackgroundView::HideScreenSaver() {
    240   SetStatusAreaVisible(true);
    241   // TODO(oshima): we need a way to suspend screen saver
    242   // to save power when it's not visible.
    243   background_area_->SetVisible(false);
    244 }
    245 
    246 bool BackgroundView::IsScreenSaverVisible() {
    247   return ScreenSaverEnabled() && background_area_->IsVisible();
    248 }
    249 
    250 bool BackgroundView::ScreenSaverEnabled() {
    251   return background_area_ != NULL;
    252 }
    253 
    254 ///////////////////////////////////////////////////////////////////////////////
    255 // BackgroundView protected:
    256 
    257 void BackgroundView::OnPaint(gfx::Canvas* canvas) {
    258   views::View::OnPaint(canvas);
    259   if (!did_paint_) {
    260     did_paint_ = true;
    261     UpdateWindowType();
    262   }
    263 }
    264 
    265 void BackgroundView::Layout() {
    266   const int kCornerPadding = 5;
    267   const int kInfoLeftPadding = 10;
    268   const int kInfoBottomPadding = 10;
    269   const int kInfoBetweenLinesPadding = 1;
    270   const int kProgressBarBottomPadding = 20;
    271   const int kProgressBarWidth = 750;
    272   const int kProgressBarHeight = 70;
    273   gfx::Size status_area_size = status_area_->GetPreferredSize();
    274   status_area_->SetBounds(
    275       width() - status_area_size.width() - kCornerPadding,
    276       kCornerPadding,
    277       status_area_size.width(),
    278       status_area_size.height());
    279   gfx::Size version_size = os_version_label_->GetPreferredSize();
    280   int os_version_y = height() - version_size.height() - kInfoBottomPadding;
    281   if (!is_official_build_)
    282     os_version_y -= (version_size.height() + kInfoBetweenLinesPadding);
    283   os_version_label_->SetBounds(
    284       kInfoLeftPadding,
    285       os_version_y,
    286       width() - 2 * kInfoLeftPadding,
    287       version_size.height());
    288   if (!is_official_build_) {
    289     boot_times_label_->SetBounds(
    290         kInfoLeftPadding,
    291         height() - (version_size.height() + kInfoBottomPadding),
    292         width() - 2 * kInfoLeftPadding,
    293         version_size.height());
    294   }
    295   if (progress_bar_) {
    296     progress_bar_->SetBounds(
    297         (width() - kProgressBarWidth) / 2,
    298         (height() - kProgressBarBottomPadding - kProgressBarHeight),
    299         kProgressBarWidth,
    300         kProgressBarHeight);
    301   }
    302   if (shutdown_button_) {
    303     shutdown_button_->LayoutIn(this);
    304   }
    305   if (background_area_)
    306     background_area_->SetBoundsRect(this->bounds());
    307 }
    308 
    309 void BackgroundView::ChildPreferredSizeChanged(View* child) {
    310   Layout();
    311   SchedulePaint();
    312 }
    313 
    314 bool BackgroundView::ShouldOpenButtonOptions(
    315     const views::View* button_view) const {
    316   if (button_view == status_area_->network_view()) {
    317     return true;
    318   }
    319   if (button_view == status_area_->clock_view() ||
    320       button_view == status_area_->input_method_view()) {
    321     return false;
    322   }
    323   return true;
    324 }
    325 
    326 void BackgroundView::OpenButtonOptions(const views::View* button_view) {
    327   if (button_view == status_area_->network_view()) {
    328     if (proxy_settings_dialog_.get() == NULL) {
    329       proxy_settings_dialog_.reset(new ProxySettingsDialog(
    330           this, GetNativeWindow()));
    331     }
    332     proxy_settings_dialog_->Show();
    333   }
    334 }
    335 
    336 StatusAreaHost::ScreenMode BackgroundView::GetScreenMode() const {
    337   return kLoginMode;
    338 }
    339 
    340 StatusAreaHost::TextStyle BackgroundView::GetTextStyle() const {
    341   return kWhitePlain;
    342 }
    343 
    344 // Overridden from LoginHtmlDialog::Delegate:
    345 void BackgroundView::OnLocaleChanged() {
    346   // Proxy settings dialog contains localized strings.
    347   proxy_settings_dialog_.reset();
    348   InitInfoLabels();
    349   SchedulePaint();
    350 }
    351 
    352 ///////////////////////////////////////////////////////////////////////////////
    353 // BackgroundView private:
    354 
    355 void BackgroundView::InitStatusArea() {
    356   DCHECK(status_area_ == NULL);
    357   status_area_ = new StatusAreaView(this);
    358   status_area_->Init();
    359   AddChildView(status_area_);
    360 }
    361 
    362 void BackgroundView::InitInfoLabels() {
    363   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    364 
    365   {
    366     int idx = GetIndexOf(os_version_label_);
    367     delete os_version_label_;
    368     os_version_label_ = new views::Label();
    369     os_version_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    370     os_version_label_->SetColor(kVersionColor);
    371     os_version_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
    372     if (idx < 0)
    373       AddChildView(os_version_label_);
    374     else
    375       AddChildViewAt(os_version_label_, idx);
    376   }
    377   if (!is_official_build_) {
    378     int idx = GetIndexOf(boot_times_label_);
    379     delete boot_times_label_;
    380     boot_times_label_ = new views::Label();
    381     boot_times_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    382     boot_times_label_->SetColor(kVersionColor);
    383     boot_times_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
    384     if (idx < 0)
    385       AddChildView(boot_times_label_);
    386     else
    387       AddChildViewAt(boot_times_label_, idx);
    388   }
    389 
    390   if (CrosLibrary::Get()->EnsureLoaded()) {
    391     version_loader_.EnablePlatformVersions(true);
    392     version_loader_.GetVersion(
    393         &version_consumer_,
    394         NewCallback(this, &BackgroundView::OnVersion),
    395         is_official_build_ ?
    396             VersionLoader::VERSION_SHORT_WITH_DATE :
    397             VersionLoader::VERSION_FULL);
    398     if (!is_official_build_) {
    399       boot_times_loader_.GetBootTimes(
    400           &boot_times_consumer_,
    401           NewCallback(this, &BackgroundView::OnBootTimes));
    402     }
    403   } else {
    404     UpdateVersionLabel();
    405   }
    406 
    407   policy::CloudPolicySubsystem* cloud_policy =
    408       g_browser_process->browser_policy_connector()->cloud_policy_subsystem();
    409   if (cloud_policy) {
    410     cloud_policy_registrar_.reset(
    411         new policy::CloudPolicySubsystem::ObserverRegistrar(
    412           cloud_policy, this));
    413 
    414     // Ensure that we have up-to-date enterprise info in case enterprise policy
    415     // is already fetched and has finished initialization.
    416     UpdateEnterpriseInfo();
    417   }
    418 }
    419 
    420 void BackgroundView::InitProgressBar() {
    421   std::vector<int> steps;
    422   steps.push_back(GetStepId(SELECT_NETWORK));
    423 #if defined(OFFICIAL_BUILD)
    424   steps.push_back(GetStepId(EULA));
    425 #endif
    426   steps.push_back(GetStepId(SIGNIN));
    427 #if defined(OFFICIAL_BUILD)
    428   if (WizardController::IsRegisterScreenDefined())
    429     steps.push_back(GetStepId(REGISTRATION));
    430 #endif
    431   steps.push_back(GetStepId(PICTURE));
    432   progress_bar_ = new OobeProgressBar(steps);
    433   AddChildView(progress_bar_);
    434 }
    435 
    436 void BackgroundView::UpdateWindowType() {
    437   std::vector<int> params;
    438   params.push_back(did_paint_ ? 1 : 0);
    439   WmIpc::instance()->SetWindowType(
    440       GTK_WIDGET(GetNativeWindow()),
    441       WM_IPC_WINDOW_LOGIN_BACKGROUND,
    442       &params);
    443 }
    444 
    445 void BackgroundView::UpdateVersionLabel() {
    446   if (!CrosLibrary::Get()->EnsureLoaded()) {
    447     os_version_label_->SetText(
    448         ASCIIToWide(CrosLibrary::Get()->load_error_string()));
    449     return;
    450   }
    451 
    452   if (version_text_.empty())
    453     return;
    454 
    455   chrome::VersionInfo version_info;
    456   std::string label_text = l10n_util::GetStringUTF8(IDS_PRODUCT_NAME);
    457   label_text += ' ';
    458   label_text += version_info.Version();
    459   label_text += " (";
    460   // TODO(rkc): Fix this. This needs to be in a resource file, but we have had
    461   // to put it in for merge into R12. Also, look at rtl implications for this
    462   // entire string composition code.
    463   label_text += kPlatformLabel;
    464   label_text += ' ';
    465   label_text += version_text_;
    466   label_text += ')';
    467 
    468   if (!enterprise_domain_text_.empty()) {
    469     label_text += ' ';
    470     if (enterprise_status_text_.empty()) {
    471       label_text += l10n_util::GetStringFUTF8(
    472           IDS_LOGIN_MANAGED_BY_LABEL_FORMAT,
    473           UTF8ToUTF16(enterprise_domain_text_));
    474     } else {
    475       label_text += l10n_util::GetStringFUTF8(
    476           IDS_LOGIN_MANAGED_BY_WITH_STATUS_LABEL_FORMAT,
    477           UTF8ToUTF16(enterprise_domain_text_),
    478           UTF8ToUTF16(enterprise_status_text_));
    479     }
    480   }
    481 
    482   // Workaround over incorrect width calculation in old fonts.
    483   // TODO(glotov): remove the following line when new fonts are used.
    484   label_text += ' ';
    485 
    486   os_version_label_->SetText(UTF8ToWide(label_text));
    487 }
    488 
    489 void BackgroundView::UpdateEnterpriseInfo() {
    490   policy::BrowserPolicyConnector* policy_connector =
    491       g_browser_process->browser_policy_connector();
    492 
    493   std::string status_text;
    494   policy::CloudPolicySubsystem* cloud_policy_subsystem =
    495       policy_connector->cloud_policy_subsystem();
    496   if (cloud_policy_subsystem) {
    497     switch (cloud_policy_subsystem->state()) {
    498       case policy::CloudPolicySubsystem::UNENROLLED:
    499         status_text = l10n_util::GetStringUTF8(
    500             IDS_LOGIN_MANAGED_BY_STATUS_PENDING);
    501         break;
    502       case policy::CloudPolicySubsystem::UNMANAGED:
    503       case policy::CloudPolicySubsystem::BAD_GAIA_TOKEN:
    504       case policy::CloudPolicySubsystem::LOCAL_ERROR:
    505         status_text = l10n_util::GetStringUTF8(
    506             IDS_LOGIN_MANAGED_BY_STATUS_LOST_CONNECTION);
    507         break;
    508       case policy::CloudPolicySubsystem::NETWORK_ERROR:
    509         status_text = l10n_util::GetStringUTF8(
    510             IDS_LOGIN_MANAGED_BY_STATUS_NETWORK_ERROR);
    511         break;
    512       case policy::CloudPolicySubsystem::TOKEN_FETCHED:
    513       case policy::CloudPolicySubsystem::SUCCESS:
    514         break;
    515     }
    516   }
    517 
    518   SetEnterpriseInfo(policy_connector->GetEnterpriseDomain(), status_text);
    519 }
    520 
    521 void BackgroundView::SetEnterpriseInfo(const std::string& domain_name,
    522                                        const std::string& status_text) {
    523   if (domain_name != enterprise_domain_text_ ||
    524       status_text != enterprise_status_text_) {
    525     enterprise_domain_text_ = domain_name;
    526     enterprise_status_text_ = status_text;
    527     UpdateVersionLabel();
    528   }
    529 }
    530 
    531 void BackgroundView::OnVersion(
    532     VersionLoader::Handle handle, std::string version) {
    533   version_text_.swap(version);
    534   UpdateVersionLabel();
    535 }
    536 
    537 void BackgroundView::OnBootTimes(
    538     BootTimesLoader::Handle handle, BootTimesLoader::BootTimes boot_times) {
    539   const char* kBootTimesNoChromeExec =
    540       "Non-firmware boot took %.2f seconds (kernel %.2fs, system %.2fs)";
    541   const char* kBootTimesChromeExec =
    542       "Non-firmware boot took %.2f seconds "
    543       "(kernel %.2fs, system %.2fs, chrome %.2fs)";
    544   std::string boot_times_text;
    545 
    546   if (boot_times.chrome > 0) {
    547     boot_times_text =
    548         base::StringPrintf(
    549             kBootTimesChromeExec,
    550             boot_times.total,
    551             boot_times.pre_startup,
    552             boot_times.system,
    553             boot_times.chrome);
    554   } else {
    555     boot_times_text =
    556         base::StringPrintf(
    557             kBootTimesNoChromeExec,
    558             boot_times.total,
    559             boot_times.pre_startup,
    560             boot_times.system);
    561   }
    562   // Use UTF8ToWide once this string is localized.
    563   boot_times_label_->SetText(ASCIIToWide(boot_times_text));
    564 }
    565 
    566 void BackgroundView::OnPolicyStateChanged(
    567     policy::CloudPolicySubsystem::PolicySubsystemState state,
    568     policy::CloudPolicySubsystem::ErrorDetails error_details) {
    569   UpdateEnterpriseInfo();
    570 }
    571 
    572 }  // namespace chromeos
    573