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