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