• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetStepId(size_t step)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:
TextButtonWithHandCursorOver(views::ButtonListener * listener,const std::wstring & text)83   TextButtonWithHandCursorOver(views::ButtonListener* listener,
84                                const std::wstring& text)
85       : views::TextButton(listener, text) {
86   }
87 
~TextButtonWithHandCursorOver()88   virtual ~TextButtonWithHandCursorOver() {}
89 
GetCursorForPoint(ui::EventType event_type,const gfx::Point & p)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.
ResetXCursor()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 
BackgroundView()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 
Init(const GURL & background_url)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 
EnableShutdownButton(bool enable)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
CreateWindowContainingView(const gfx::Rect & bounds,const GURL & background_url,BackgroundView ** view)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 
CreateModalPopup(views::WindowDelegate * view)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 
GetNativeWindow() const203 gfx::NativeWindow BackgroundView::GetNativeWindow() const {
204   return
205       GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
206 }
207 
SetStatusAreaVisible(bool visible)208 void BackgroundView::SetStatusAreaVisible(bool visible) {
209   status_area_->SetVisible(visible);
210 }
211 
SetStatusAreaEnabled(bool enable)212 void BackgroundView::SetStatusAreaEnabled(bool enable) {
213   status_area_->MakeButtonsActive(enable);
214 }
215 
SetOobeProgressBarVisible(bool visible)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 
IsOobeProgressBarVisible()224 bool BackgroundView::IsOobeProgressBarVisible() {
225   return progress_bar_ && progress_bar_->IsVisible();
226 }
227 
SetOobeProgress(LoginStep step)228 void BackgroundView::SetOobeProgress(LoginStep step) {
229   DCHECK(step < STEPS_COUNT);
230   if (progress_bar_)
231     progress_bar_->SetStep(GetStepId(step));
232 }
233 
ShowScreenSaver()234 void BackgroundView::ShowScreenSaver() {
235   SetStatusAreaVisible(false);
236   background_area_->SetVisible(true);
237 }
238 
HideScreenSaver()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 
IsScreenSaverVisible()246 bool BackgroundView::IsScreenSaverVisible() {
247   return ScreenSaverEnabled() && background_area_->IsVisible();
248 }
249 
ScreenSaverEnabled()250 bool BackgroundView::ScreenSaverEnabled() {
251   return background_area_ != NULL;
252 }
253 
254 ///////////////////////////////////////////////////////////////////////////////
255 // BackgroundView protected:
256 
OnPaint(gfx::Canvas * canvas)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 
Layout()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 
ChildPreferredSizeChanged(View * child)309 void BackgroundView::ChildPreferredSizeChanged(View* child) {
310   Layout();
311   SchedulePaint();
312 }
313 
ShouldOpenButtonOptions(const views::View * button_view) const314 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 
OpenButtonOptions(const views::View * button_view)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 
GetScreenMode() const336 StatusAreaHost::ScreenMode BackgroundView::GetScreenMode() const {
337   return kLoginMode;
338 }
339 
GetTextStyle() const340 StatusAreaHost::TextStyle BackgroundView::GetTextStyle() const {
341   return kWhitePlain;
342 }
343 
344 // Overridden from LoginHtmlDialog::Delegate:
OnLocaleChanged()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 
InitStatusArea()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 
InitInfoLabels()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 
InitProgressBar()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 
UpdateWindowType()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 
UpdateVersionLabel()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 
UpdateEnterpriseInfo()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 
SetEnterpriseInfo(const std::string & domain_name,const std::string & status_text)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 
OnVersion(VersionLoader::Handle handle,std::string version)531 void BackgroundView::OnVersion(
532     VersionLoader::Handle handle, std::string version) {
533   version_text_.swap(version);
534   UpdateVersionLabel();
535 }
536 
OnBootTimes(BootTimesLoader::Handle handle,BootTimesLoader::BootTimes boot_times)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 
OnPolicyStateChanged(policy::CloudPolicySubsystem::PolicySubsystemState state,policy::CloudPolicySubsystem::ErrorDetails error_details)566 void BackgroundView::OnPolicyStateChanged(
567     policy::CloudPolicySubsystem::PolicySubsystemState state,
568     policy::CloudPolicySubsystem::ErrorDetails error_details) {
569   UpdateEnterpriseInfo();
570 }
571 
572 }  // namespace chromeos
573