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/ui/views/about_chrome_view.h" 6 7 #if defined(OS_WIN) 8 #include <commdlg.h> 9 #endif // defined(OS_WIN) 10 11 #include <algorithm> 12 #include <string> 13 #include <vector> 14 15 #include "base/callback.h" 16 #include "base/i18n/rtl.h" 17 #include "base/string_number_conversions.h" 18 #include "base/utf_string_conversions.h" 19 #include "base/win/windows_version.h" 20 #include "chrome/browser/google/google_util.h" 21 #include "chrome/browser/metrics/user_metrics.h" 22 #include "chrome/browser/platform_util.h" 23 #include "chrome/browser/prefs/pref_service.h" 24 #include "chrome/browser/ui/browser_list.h" 25 #include "chrome/browser/ui/views/window.h" 26 #include "chrome/common/chrome_constants.h" 27 #include "chrome/common/chrome_version_info.h" 28 #include "chrome/common/pref_names.h" 29 #include "chrome/common/url_constants.h" 30 #include "chrome/installer/util/browser_distribution.h" 31 #include "grit/chromium_strings.h" 32 #include "grit/generated_resources.h" 33 #include "grit/locale_settings.h" 34 #include "grit/theme_resources.h" 35 #include "ui/base/l10n/l10n_util.h" 36 #include "ui/base/resource/resource_bundle.h" 37 #include "ui/gfx/canvas.h" 38 #include "views/controls/textfield/textfield.h" 39 #include "views/controls/throbber.h" 40 #include "views/layout/layout_constants.h" 41 #include "views/view_text_utils.h" 42 #include "views/widget/widget.h" 43 #include "views/window/window.h" 44 #include "webkit/glue/webkit_glue.h" 45 46 #if defined(OS_WIN) 47 #include "base/win/win_util.h" 48 #include "chrome/browser/ui/views/restart_message_box.h" 49 #include "chrome/installer/util/install_util.h" 50 #endif // defined(OS_WIN) 51 52 #if defined(OS_WIN) || defined(OS_CHROMEOS) 53 #include "chrome/browser/browser_process.h" 54 #endif // defined(OS_WIN) || defined(OS_CHROMEOS) 55 56 namespace { 57 // The pixel width of the version text field. Ideally, we'd like to have the 58 // bounds set to the edge of the icon. However, the icon is not a view but a 59 // part of the background, so we have to hard code the width to make sure 60 // the version field doesn't overlap it. 61 const int kVersionFieldWidth = 195; 62 63 // These are used as placeholder text around the links in the text in the about 64 // dialog. 65 const wchar_t* kBeginLink = L"BEGIN_LINK"; 66 const wchar_t* kEndLink = L"END_LINK"; 67 const wchar_t* kBeginLinkChr = L"BEGIN_LINK_CHR"; 68 const wchar_t* kBeginLinkOss = L"BEGIN_LINK_OSS"; 69 const wchar_t* kEndLinkChr = L"END_LINK_CHR"; 70 const wchar_t* kEndLinkOss = L"END_LINK_OSS"; 71 72 // The background bitmap used to draw the background color for the About box 73 // and the separator line (this is the image we will draw the logo on top of). 74 static const SkBitmap* kBackgroundBmp = NULL; 75 76 // Returns a substring from |text| between start and end. 77 std::wstring StringSubRange(const std::wstring& text, size_t start, 78 size_t end) { 79 DCHECK(end > start); 80 return text.substr(start, end - start); 81 } 82 83 } // namespace 84 85 namespace browser { 86 87 // Declared in browser_dialogs.h so that others don't 88 // need to depend on our .h. 89 views::Window* ShowAboutChromeView(gfx::NativeWindow parent, 90 Profile* profile) { 91 views::Window* about_chrome_window = 92 browser::CreateViewsWindow(parent, 93 gfx::Rect(), 94 new AboutChromeView(profile)); 95 about_chrome_window->Show(); 96 return about_chrome_window; 97 } 98 99 } // namespace browser 100 101 //////////////////////////////////////////////////////////////////////////////// 102 // AboutChromeView, public: 103 104 AboutChromeView::AboutChromeView(Profile* profile) 105 : profile_(profile), 106 about_dlg_background_logo_(NULL), 107 about_title_label_(NULL), 108 version_label_(NULL), 109 #if defined(OS_CHROMEOS) 110 os_version_label_(NULL), 111 #endif 112 copyright_label_(NULL), 113 main_text_label_(NULL), 114 main_text_label_height_(0), 115 chromium_url_(NULL), 116 open_source_url_(NULL), 117 terms_of_service_url_(NULL), 118 restart_button_visible_(false), 119 chromium_url_appears_first_(true), 120 text_direction_is_rtl_(false) { 121 DCHECK(profile); 122 #if defined(OS_CHROMEOS) 123 loader_.GetVersion(&consumer_, 124 NewCallback(this, &AboutChromeView::OnOSVersion), 125 chromeos::VersionLoader::VERSION_FULL); 126 #endif 127 Init(); 128 129 #if defined(OS_WIN) || defined(OS_CHROMEOS) 130 google_updater_ = new GoogleUpdate(); 131 google_updater_->set_status_listener(this); 132 #endif 133 134 if (kBackgroundBmp == NULL) { 135 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 136 kBackgroundBmp = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND_COLOR); 137 } 138 } 139 140 AboutChromeView::~AboutChromeView() { 141 #if defined(OS_WIN) || defined(OS_CHROMEOS) 142 // The Google Updater will hold a pointer to us until it reports status, so we 143 // need to let it know that we will no longer be listening. 144 if (google_updater_) 145 google_updater_->set_status_listener(NULL); 146 #endif 147 } 148 149 void AboutChromeView::Init() { 150 text_direction_is_rtl_ = base::i18n::IsRTL(); 151 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 152 153 chrome::VersionInfo version_info; 154 if (!version_info.is_valid()) { 155 NOTREACHED() << L"Failed to initialize about window"; 156 return; 157 } 158 159 current_version_ = version_info.Version(); 160 161 // This code only runs as a result of the user opening the About box so 162 // doing registry access to get the version string modifier should be fine. 163 base::ThreadRestrictions::ScopedAllowIO allow_io; 164 std::string version_modifier = platform_util::GetVersionStringModifier(); 165 if (!version_modifier.empty()) 166 version_details_ += " " + version_modifier; 167 168 #if !defined(GOOGLE_CHROME_BUILD) 169 version_details_ += " ("; 170 version_details_ += version_info.LastChange(); 171 version_details_ += ")"; 172 #endif 173 174 // Views we will add to the *parent* of this dialog, since it will display 175 // next to the buttons which we don't draw ourselves. 176 throbber_.reset(new views::Throbber(50, true)); 177 throbber_->set_parent_owned(false); 178 throbber_->SetVisible(false); 179 180 SkBitmap* success_image = rb.GetBitmapNamed(IDR_UPDATE_UPTODATE); 181 success_indicator_.SetImage(*success_image); 182 success_indicator_.set_parent_owned(false); 183 184 SkBitmap* update_available_image = rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE); 185 update_available_indicator_.SetImage(*update_available_image); 186 update_available_indicator_.set_parent_owned(false); 187 188 SkBitmap* timeout_image = rb.GetBitmapNamed(IDR_UPDATE_FAIL); 189 timeout_indicator_.SetImage(*timeout_image); 190 timeout_indicator_.set_parent_owned(false); 191 192 update_label_.SetVisible(false); 193 update_label_.set_parent_owned(false); 194 195 // Regular view controls we draw by ourself. First, we add the background 196 // image for the dialog. We have two different background bitmaps, one for 197 // LTR UIs and one for RTL UIs. We load the correct bitmap based on the UI 198 // layout of the view. 199 about_dlg_background_logo_ = new views::ImageView(); 200 SkBitmap* about_background_logo = rb.GetBitmapNamed(base::i18n::IsRTL() ? 201 IDR_ABOUT_BACKGROUND_RTL : IDR_ABOUT_BACKGROUND); 202 203 about_dlg_background_logo_->SetImage(*about_background_logo); 204 AddChildView(about_dlg_background_logo_); 205 206 // Add the dialog labels. 207 about_title_label_ = new views::Label( 208 UTF16ToWide(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 209 about_title_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont( 210 ResourceBundle::BaseFont).DeriveFont(18)); 211 about_title_label_->SetColor(SK_ColorBLACK); 212 AddChildView(about_title_label_); 213 214 // This is a text field so people can copy the version number from the dialog. 215 version_label_ = new views::Textfield(); 216 version_label_->SetText(ASCIIToUTF16(current_version_ + version_details_)); 217 version_label_->SetReadOnly(true); 218 version_label_->RemoveBorder(); 219 version_label_->SetTextColor(SK_ColorBLACK); 220 version_label_->SetBackgroundColor(SK_ColorWHITE); 221 version_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont( 222 ResourceBundle::BaseFont)); 223 AddChildView(version_label_); 224 225 #if defined(OS_CHROMEOS) 226 os_version_label_ = new views::Textfield(views::Textfield::STYLE_MULTILINE); 227 os_version_label_->SetReadOnly(true); 228 os_version_label_->RemoveBorder(); 229 os_version_label_->SetTextColor(SK_ColorBLACK); 230 os_version_label_->SetBackgroundColor(SK_ColorWHITE); 231 os_version_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont( 232 ResourceBundle::BaseFont)); 233 AddChildView(os_version_label_); 234 #endif 235 236 // The copyright URL portion of the main label. 237 copyright_label_ = new views::Label( 238 UTF16ToWide(l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT))); 239 copyright_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); 240 AddChildView(copyright_label_); 241 242 main_text_label_ = new views::Label(L""); 243 244 // Figure out what to write in the main label of the About box. 245 std::wstring text = 246 UTF16ToWide(l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_LICENSE)); 247 248 chromium_url_appears_first_ = 249 text.find(kBeginLinkChr) < text.find(kBeginLinkOss); 250 251 size_t link1 = text.find(kBeginLink); 252 DCHECK(link1 != std::wstring::npos); 253 size_t link1_end = text.find(kEndLink, link1); 254 DCHECK(link1_end != std::wstring::npos); 255 size_t link2 = text.find(kBeginLink, link1_end); 256 DCHECK(link2 != std::wstring::npos); 257 size_t link2_end = text.find(kEndLink, link2); 258 DCHECK(link1_end != std::wstring::npos); 259 260 main_label_chunk1_ = text.substr(0, link1); 261 main_label_chunk2_ = StringSubRange(text, link1_end + wcslen(kEndLinkOss), 262 link2); 263 main_label_chunk3_ = text.substr(link2_end + wcslen(kEndLinkOss)); 264 265 // The Chromium link within the main text of the dialog. 266 chromium_url_ = new views::Link( 267 StringSubRange(text, text.find(kBeginLinkChr) + wcslen(kBeginLinkChr), 268 text.find(kEndLinkChr))); 269 AddChildView(chromium_url_); 270 chromium_url_->SetController(this); 271 272 // The Open Source link within the main text of the dialog. 273 open_source_url_ = new views::Link( 274 StringSubRange(text, text.find(kBeginLinkOss) + wcslen(kBeginLinkOss), 275 text.find(kEndLinkOss))); 276 AddChildView(open_source_url_); 277 open_source_url_->SetController(this); 278 279 // Add together all the strings in the dialog for the purpose of calculating 280 // the height of the dialog. The space for the Terms of Service string is not 281 // included (it is added later, if needed). 282 std::wstring full_text = main_label_chunk1_ + chromium_url_->GetText() + 283 main_label_chunk2_ + open_source_url_->GetText() + 284 main_label_chunk3_; 285 286 dialog_dimensions_ = views::Window::GetLocalizedContentsSize( 287 IDS_ABOUT_DIALOG_WIDTH_CHARS, 288 IDS_ABOUT_DIALOG_MINIMUM_HEIGHT_LINES); 289 290 // Create a label and add the full text so we can query it for the height. 291 views::Label dummy_text(full_text); 292 dummy_text.SetMultiLine(true); 293 gfx::Font font = 294 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); 295 296 // Add up the height of the various elements on the page. 297 int height = about_background_logo->height() + 298 views::kRelatedControlVerticalSpacing + 299 // Copyright line. 300 font.GetHeight() + 301 // Main label. 302 dummy_text.GetHeightForWidth( 303 dialog_dimensions_.width() - (2 * views::kPanelHorizMargin)) + 304 views::kRelatedControlVerticalSpacing; 305 306 #if defined(GOOGLE_CHROME_BUILD) 307 std::vector<size_t> url_offsets; 308 text = UTF16ToWide(l10n_util::GetStringFUTF16(IDS_ABOUT_TERMS_OF_SERVICE, 309 string16(), 310 string16(), 311 &url_offsets)); 312 313 main_label_chunk4_ = text.substr(0, url_offsets[0]); 314 main_label_chunk5_ = text.substr(url_offsets[0]); 315 316 // The Terms of Service URL at the bottom. 317 terms_of_service_url_ = new views::Link( 318 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TERMS_OF_SERVICE))); 319 AddChildView(terms_of_service_url_); 320 terms_of_service_url_->SetController(this); 321 322 // Add the Terms of Service line and some whitespace. 323 height += font.GetHeight() + views::kRelatedControlVerticalSpacing; 324 #endif 325 326 // Use whichever is greater (the calculated height or the specified minimum 327 // height). 328 dialog_dimensions_.set_height(std::max(height, dialog_dimensions_.height())); 329 } 330 331 //////////////////////////////////////////////////////////////////////////////// 332 // AboutChromeView, views::View implementation: 333 334 gfx::Size AboutChromeView::GetPreferredSize() { 335 return dialog_dimensions_; 336 } 337 338 void AboutChromeView::Layout() { 339 gfx::Size panel_size = GetPreferredSize(); 340 341 // Background image for the dialog. 342 gfx::Size sz = about_dlg_background_logo_->GetPreferredSize(); 343 // Used to position main text below. 344 int background_image_height = sz.height(); 345 about_dlg_background_logo_->SetBounds(panel_size.width() - sz.width(), 0, 346 sz.width(), sz.height()); 347 348 // First label goes to the top left corner. 349 sz = about_title_label_->GetPreferredSize(); 350 about_title_label_->SetBounds( 351 views::kPanelHorizMargin, views::kPanelVertMargin, 352 sz.width(), sz.height()); 353 354 // Then we have the version number right below it. 355 sz = version_label_->GetPreferredSize(); 356 version_label_->SetBounds(views::kPanelHorizMargin, 357 about_title_label_->y() + 358 about_title_label_->height() + 359 views::kRelatedControlVerticalSpacing, 360 kVersionFieldWidth, 361 sz.height()); 362 363 #if defined(OS_CHROMEOS) 364 // Then we have the version number right below it. 365 sz = os_version_label_->GetPreferredSize(); 366 os_version_label_->SetBounds( 367 views::kPanelHorizMargin, 368 version_label_->y() + 369 version_label_->height() + 370 views::kRelatedControlVerticalSpacing, 371 kVersionFieldWidth, 372 sz.height()); 373 #endif 374 375 // For the width of the main text label we want to use up the whole panel 376 // width and remaining height, minus a little margin on each side. 377 int y_pos = background_image_height + views::kRelatedControlVerticalSpacing; 378 sz.set_width(panel_size.width() - 2 * views::kPanelHorizMargin); 379 380 // Draw the text right below the background image. 381 copyright_label_->SetBounds(views::kPanelHorizMargin, 382 y_pos, 383 sz.width(), 384 sz.height()); 385 386 // Then the main_text_label. 387 main_text_label_->SetBounds(views::kPanelHorizMargin, 388 copyright_label_->y() + 389 copyright_label_->height(), 390 sz.width(), 391 main_text_label_height_); 392 393 // Get the y-coordinate of our parent so we can position the text left of the 394 // buttons at the bottom. 395 gfx::Rect parent_bounds = parent()->GetContentsBounds(); 396 397 sz = throbber_->GetPreferredSize(); 398 int throbber_topleft_x = views::kPanelHorizMargin; 399 int throbber_topleft_y = 400 parent_bounds.bottom() - sz.height() - views::kButtonVEdgeMargin - 3; 401 throbber_->SetBounds(throbber_topleft_x, throbber_topleft_y, 402 sz.width(), sz.height()); 403 404 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. 405 sz = success_indicator_.GetPreferredSize(); 406 success_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, 407 sz.width(), sz.height()); 408 409 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. 410 sz = update_available_indicator_.GetPreferredSize(); 411 update_available_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, 412 sz.width(), sz.height()); 413 414 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. 415 sz = timeout_indicator_.GetPreferredSize(); 416 timeout_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, 417 sz.width(), sz.height()); 418 419 // The update label should be at the bottom of the screen, to the right of 420 // the throbber. We specify width to the end of the dialog because it contains 421 // variable length messages. 422 sz = update_label_.GetPreferredSize(); 423 int update_label_x = throbber_->x() + throbber_->width() + 424 views::kRelatedControlHorizontalSpacing; 425 update_label_.SetHorizontalAlignment(views::Label::ALIGN_LEFT); 426 update_label_.SetBounds(update_label_x, 427 throbber_topleft_y + 1, 428 parent_bounds.width() - update_label_x, 429 sz.height()); 430 } 431 432 433 void AboutChromeView::OnPaint(gfx::Canvas* canvas) { 434 views::View::OnPaint(canvas); 435 436 // Draw the background image color (and the separator) across the dialog. 437 // This will become the background for the logo image at the top of the 438 // dialog. 439 canvas->TileImageInt(*kBackgroundBmp, 0, 0, 440 dialog_dimensions_.width(), kBackgroundBmp->height()); 441 442 gfx::Font font = 443 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); 444 445 const gfx::Rect label_bounds = main_text_label_->bounds(); 446 447 views::Link* link1 = 448 chromium_url_appears_first_ ? chromium_url_ : open_source_url_; 449 views::Link* link2 = 450 chromium_url_appears_first_ ? open_source_url_ : chromium_url_; 451 gfx::Rect* rect1 = chromium_url_appears_first_ ? 452 &chromium_url_rect_ : &open_source_url_rect_; 453 gfx::Rect* rect2 = chromium_url_appears_first_ ? 454 &open_source_url_rect_ : &chromium_url_rect_; 455 456 // This struct keeps track of where to write the next word (which x,y 457 // pixel coordinate). This struct is updated after drawing text and checking 458 // if we need to wrap. 459 gfx::Size position; 460 // Draw the first text chunk and position the Chromium url. 461 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, 462 main_label_chunk1_, link1, rect1, &position, text_direction_is_rtl_, 463 label_bounds, font); 464 // Draw the second text chunk and position the Open Source url. 465 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, 466 main_label_chunk2_, link2, rect2, &position, text_direction_is_rtl_, 467 label_bounds, font); 468 // Draw the third text chunk (which has no URL associated with it). 469 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, 470 main_label_chunk3_, NULL, NULL, &position, text_direction_is_rtl_, 471 label_bounds, font); 472 473 #if defined(GOOGLE_CHROME_BUILD) 474 // Insert a line break and some whitespace. 475 position.set_width(0); 476 position.Enlarge(0, font.GetHeight() + views::kRelatedControlVerticalSpacing); 477 478 // And now the Terms of Service and position the TOS url. 479 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, 480 main_label_chunk4_, terms_of_service_url_, &terms_of_service_url_rect_, 481 &position, text_direction_is_rtl_, label_bounds, font); 482 // The last text chunk doesn't have a URL associated with it. 483 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, 484 main_label_chunk5_, NULL, NULL, &position, text_direction_is_rtl_, 485 label_bounds, font); 486 487 // Position the TOS URL within the main label. 488 terms_of_service_url_->SetBounds(terms_of_service_url_rect_.x(), 489 terms_of_service_url_rect_.y(), 490 terms_of_service_url_rect_.width(), 491 terms_of_service_url_rect_.height()); 492 #endif 493 494 // Position the URLs within the main label. First position the Chromium URL 495 // within the main label. 496 chromium_url_->SetBounds(chromium_url_rect_.x(), 497 chromium_url_rect_.y(), 498 chromium_url_rect_.width(), 499 chromium_url_rect_.height()); 500 // Then position the Open Source URL within the main label. 501 open_source_url_->SetBounds(open_source_url_rect_.x(), 502 open_source_url_rect_.y(), 503 open_source_url_rect_.width(), 504 open_source_url_rect_.height()); 505 506 // Save the height so we can set the bounds correctly. 507 main_text_label_height_ = position.height() + font.GetHeight(); 508 } 509 510 void AboutChromeView::ViewHierarchyChanged(bool is_add, 511 views::View* parent, 512 views::View* child) { 513 // Since we want some of the controls to show up in the same visual row 514 // as the buttons, which are provided by the framework, we must add the 515 // buttons to the non-client view, which is the parent of this view. 516 // Similarly, when we're removed from the view hierarchy, we must take care 517 // to remove these items as well. 518 if (child == this) { 519 if (is_add) { 520 parent->AddChildView(&update_label_); 521 parent->AddChildView(throbber_.get()); 522 parent->AddChildView(&success_indicator_); 523 success_indicator_.SetVisible(false); 524 parent->AddChildView(&update_available_indicator_); 525 update_available_indicator_.SetVisible(false); 526 parent->AddChildView(&timeout_indicator_); 527 timeout_indicator_.SetVisible(false); 528 529 #if defined(OS_WIN) 530 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned 531 // off. So, in this case we just want the About box to not mention 532 // on-demand updates. Silent updates (in the background) should still 533 // work as before - enabling UAC or installing the latest service pack 534 // for Vista is another option. 535 if (!(base::win::GetVersion() == base::win::VERSION_VISTA && 536 (base::win::OSInfo::GetInstance()->service_pack().major == 0) && 537 !base::win::UserAccountControlIsEnabled())) { 538 UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR); 539 // CheckForUpdate(false, ...) means don't upgrade yet. 540 google_updater_->CheckForUpdate(false, window()); 541 } 542 #elif defined(OS_CHROMEOS) 543 UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR); 544 // CheckForUpdate(false, ...) means don't upgrade yet. 545 google_updater_->CheckForUpdate(false, window()); 546 #endif 547 } else { 548 parent->RemoveChildView(&update_label_); 549 parent->RemoveChildView(throbber_.get()); 550 parent->RemoveChildView(&success_indicator_); 551 parent->RemoveChildView(&update_available_indicator_); 552 parent->RemoveChildView(&timeout_indicator_); 553 } 554 } 555 } 556 557 //////////////////////////////////////////////////////////////////////////////// 558 // AboutChromeView, views::DialogDelegate implementation: 559 560 std::wstring AboutChromeView::GetDialogButtonLabel( 561 MessageBoxFlags::DialogButton button) const { 562 if (button == MessageBoxFlags::DIALOGBUTTON_OK) { 563 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_RELAUNCH_AND_UPDATE)); 564 } else if (button == MessageBoxFlags::DIALOGBUTTON_CANCEL) { 565 if (restart_button_visible_) 566 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_NOT_NOW)); 567 // The OK button (which is the default button) has been re-purposed to be 568 // 'Restart Now' so we want the Cancel button should have the label 569 // OK but act like a Cancel button in all other ways. 570 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_OK)); 571 } 572 573 NOTREACHED(); 574 return L""; 575 } 576 577 std::wstring AboutChromeView::GetWindowTitle() const { 578 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_ABOUT_CHROME_TITLE)); 579 } 580 581 bool AboutChromeView::IsDialogButtonEnabled( 582 MessageBoxFlags::DialogButton button) const { 583 if (button == MessageBoxFlags::DIALOGBUTTON_OK && !restart_button_visible_) 584 return false; 585 586 return true; 587 } 588 589 bool AboutChromeView::IsDialogButtonVisible( 590 MessageBoxFlags::DialogButton button) const { 591 if (button == MessageBoxFlags::DIALOGBUTTON_OK && !restart_button_visible_) 592 return false; 593 594 return true; 595 } 596 597 // (on ChromeOS) the default focus is ending up in the version field when 598 // the update button is hidden. This forces the focus to always be on the 599 // OK button (which is the dialog cancel button, see GetDialogButtonLabel 600 // above). 601 int AboutChromeView::GetDefaultDialogButton() const { 602 return MessageBoxFlags::DIALOGBUTTON_CANCEL; 603 } 604 605 bool AboutChromeView::CanResize() const { 606 return false; 607 } 608 609 bool AboutChromeView::CanMaximize() const { 610 return false; 611 } 612 613 bool AboutChromeView::IsAlwaysOnTop() const { 614 return false; 615 } 616 617 bool AboutChromeView::HasAlwaysOnTopMenu() const { 618 return false; 619 } 620 621 bool AboutChromeView::IsModal() const { 622 return true; 623 } 624 625 bool AboutChromeView::Accept() { 626 #if defined(OS_WIN) || defined(OS_CHROMEOS) 627 // Set the flag to restore the last session on shutdown. 628 PrefService* pref_service = g_browser_process->local_state(); 629 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); 630 BrowserList::CloseAllBrowsersAndExit(); 631 #endif 632 633 return true; 634 } 635 636 views::View* AboutChromeView::GetContentsView() { 637 return this; 638 } 639 640 //////////////////////////////////////////////////////////////////////////////// 641 // AboutChromeView, views::LinkController implementation: 642 643 void AboutChromeView::LinkActivated(views::Link* source, 644 int event_flags) { 645 GURL url; 646 if (source == terms_of_service_url_) { 647 url = GURL(chrome::kAboutTermsURL); 648 } else if (source == chromium_url_) { 649 url = google_util::AppendGoogleLocaleParam( 650 GURL(chrome::kChromiumProjectURL)); 651 } else if (source == open_source_url_) { 652 url = GURL(chrome::kAboutCreditsURL); 653 } else { 654 NOTREACHED() << "Unknown link source"; 655 } 656 657 Browser* browser = BrowserList::GetLastActive(); 658 #if defined(OS_CHROMEOS) 659 browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); 660 #else 661 browser->OpenURL(url, GURL(), NEW_WINDOW, PageTransition::LINK); 662 #endif 663 } 664 665 #if defined(OS_CHROMEOS) 666 void AboutChromeView::OnOSVersion( 667 chromeos::VersionLoader::Handle handle, 668 std::string version) { 669 670 // This is a hack to "wrap" the very long Test Build version after 671 // the version number, the remaining text won't be visible but can 672 // be selected, copied, pasted. 673 std::string::size_type pos = version.find(" (Test Build"); 674 if (pos != std::string::npos) 675 version.replace(pos, 1, "\n"); 676 677 os_version_label_->SetText(UTF8ToUTF16(version)); 678 } 679 #endif 680 681 #if defined(OS_WIN) || defined(OS_CHROMEOS) 682 //////////////////////////////////////////////////////////////////////////////// 683 // AboutChromeView, GoogleUpdateStatusListener implementation: 684 685 void AboutChromeView::OnReportResults(GoogleUpdateUpgradeResult result, 686 GoogleUpdateErrorCode error_code, 687 const std::wstring& version) { 688 // Drop the last reference to the object so that it gets cleaned up here. 689 google_updater_ = NULL; 690 691 // Make a note of which version Google Update is reporting is the latest 692 // version. 693 new_version_available_ = version; 694 UpdateStatus(result, error_code); 695 } 696 //////////////////////////////////////////////////////////////////////////////// 697 // AboutChromeView, private: 698 699 void AboutChromeView::UpdateStatus(GoogleUpdateUpgradeResult result, 700 GoogleUpdateErrorCode error_code) { 701 #if !defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS) 702 // For Chromium builds it would show an error message. 703 // But it looks weird because in fact there is no error, 704 // just the update server is not available for non-official builds. 705 return; 706 #endif 707 bool show_success_indicator = false; 708 bool show_update_available_indicator = false; 709 bool show_timeout_indicator = false; 710 bool show_throbber = false; 711 bool show_update_label = true; // Always visible, except at start. 712 713 switch (result) { 714 case UPGRADE_STARTED: 715 UserMetrics::RecordAction(UserMetricsAction("Upgrade_Started"), profile_); 716 show_throbber = true; 717 update_label_.SetText( 718 UTF16ToWide(l10n_util::GetStringUTF16(IDS_UPGRADE_STARTED))); 719 break; 720 case UPGRADE_CHECK_STARTED: 721 UserMetrics::RecordAction(UserMetricsAction("UpgradeCheck_Started"), 722 profile_); 723 show_throbber = true; 724 update_label_.SetText( 725 UTF16ToWide(l10n_util::GetStringUTF16(IDS_UPGRADE_CHECK_STARTED))); 726 break; 727 case UPGRADE_IS_AVAILABLE: 728 UserMetrics::RecordAction( 729 UserMetricsAction("UpgradeCheck_UpgradeIsAvailable"), profile_); 730 DCHECK(!google_updater_); // Should have been nulled out already. 731 google_updater_ = new GoogleUpdate(); 732 google_updater_->set_status_listener(this); 733 UpdateStatus(UPGRADE_STARTED, GOOGLE_UPDATE_NO_ERROR); 734 // CheckForUpdate(true,...) means perform upgrade if new version found. 735 google_updater_->CheckForUpdate(true, window()); 736 // TODO(seanparent): Need to see if this code needs to change to 737 // force a machine restart. 738 return; 739 case UPGRADE_ALREADY_UP_TO_DATE: { 740 // The extra version check is necessary on Windows because the application 741 // may be already up to date on disk though the running app is still 742 // out of date. Chrome OS doesn't quite have this issue since the 743 // OS/App are updated together. If a newer version of the OS has been 744 // staged then UPGRADE_SUCESSFUL will be returned. 745 #if defined(OS_WIN) 746 // Google Update reported that Chrome is up-to-date. Now make sure that we 747 // are running the latest version and if not, notify the user by falling 748 // into the next case of UPGRADE_SUCCESSFUL. 749 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 750 base::ThreadRestrictions::ScopedAllowIO allow_io; 751 scoped_ptr<Version> installed_version( 752 InstallUtil::GetChromeVersion(dist, false)); 753 if (!installed_version.get()) { 754 // User-level Chrome is not installed, check system-level. 755 installed_version.reset(InstallUtil::GetChromeVersion(dist, true)); 756 } 757 scoped_ptr<Version> running_version( 758 Version::GetVersionFromString(current_version_)); 759 if (!installed_version.get() || 760 (installed_version->CompareTo(*running_version) <= 0)) { 761 #endif 762 UserMetrics::RecordAction( 763 UserMetricsAction("UpgradeCheck_AlreadyUpToDate"), profile_); 764 #if defined(OS_CHROMEOS) 765 std::wstring update_label_text = UTF16ToWide(l10n_util::GetStringFUTF16( 766 IDS_UPGRADE_ALREADY_UP_TO_DATE, 767 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 768 #else 769 std::wstring update_label_text = l10n_util::GetStringFUTF16( 770 IDS_UPGRADE_ALREADY_UP_TO_DATE, 771 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 772 ASCIIToUTF16(current_version_)); 773 #endif 774 if (base::i18n::IsRTL()) { 775 update_label_text.push_back( 776 static_cast<wchar_t>(base::i18n::kLeftToRightMark)); 777 } 778 update_label_.SetText(update_label_text); 779 show_success_indicator = true; 780 break; 781 #if defined(OS_WIN) 782 } 783 #endif 784 // No break here as we want to notify user about upgrade if there is one. 785 } 786 case UPGRADE_SUCCESSFUL: { 787 if (result == UPGRADE_ALREADY_UP_TO_DATE) 788 UserMetrics::RecordAction( 789 UserMetricsAction("UpgradeCheck_AlreadyUpgraded"), profile_); 790 else 791 UserMetrics::RecordAction(UserMetricsAction("UpgradeCheck_Upgraded"), 792 profile_); 793 restart_button_visible_ = true; 794 const std::wstring& update_string = 795 UTF16ToWide(l10n_util::GetStringFUTF16( 796 IDS_UPGRADE_SUCCESSFUL_RELAUNCH, 797 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 798 update_label_.SetText(update_string); 799 show_success_indicator = true; 800 break; 801 } 802 case UPGRADE_ERROR: 803 UserMetrics::RecordAction(UserMetricsAction("UpgradeCheck_Error"), 804 profile_); 805 restart_button_visible_ = false; 806 if (error_code != GOOGLE_UPDATE_DISABLED_BY_POLICY) { 807 update_label_.SetText(UTF16ToWide( 808 l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, error_code))); 809 } else { 810 update_label_.SetText(UTF16ToWide( 811 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY))); 812 } 813 show_timeout_indicator = true; 814 break; 815 default: 816 NOTREACHED(); 817 } 818 819 success_indicator_.SetVisible(show_success_indicator); 820 update_available_indicator_.SetVisible(show_update_available_indicator); 821 timeout_indicator_.SetVisible(show_timeout_indicator); 822 update_label_.SetVisible(show_update_label); 823 throbber_->SetVisible(show_throbber); 824 if (show_throbber) 825 throbber_->Start(); 826 else 827 throbber_->Stop(); 828 829 // We have updated controls on the parent, so we need to update its layout. 830 parent()->Layout(); 831 832 // Check button may have appeared/disappeared. We cannot call this during 833 // ViewHierarchyChanged because the |window()| pointer hasn't been set yet. 834 if (window()) 835 GetDialogClientView()->UpdateDialogButtons(); 836 } 837 838 #endif 839