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 ¶ms);
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