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