Home | History | Annotate | Download | only in views
      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