• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
StringSubRange(const std::wstring & text,size_t start,size_t 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.
ShowAboutChromeView(gfx::NativeWindow parent,Profile * profile)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 
AboutChromeView(Profile * profile)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 
~AboutChromeView()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 
Init()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 
GetPreferredSize()334 gfx::Size AboutChromeView::GetPreferredSize() {
335   return dialog_dimensions_;
336 }
337 
Layout()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 
OnPaint(gfx::Canvas * canvas)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 
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)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 
GetDialogButtonLabel(MessageBoxFlags::DialogButton button) const560 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 
GetWindowTitle() const577 std::wstring AboutChromeView::GetWindowTitle() const {
578   return UTF16ToWide(l10n_util::GetStringUTF16(IDS_ABOUT_CHROME_TITLE));
579 }
580 
IsDialogButtonEnabled(MessageBoxFlags::DialogButton button) const581 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 
IsDialogButtonVisible(MessageBoxFlags::DialogButton button) const589 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).
GetDefaultDialogButton() const601 int AboutChromeView::GetDefaultDialogButton() const {
602   return MessageBoxFlags::DIALOGBUTTON_CANCEL;
603 }
604 
CanResize() const605 bool AboutChromeView::CanResize() const {
606   return false;
607 }
608 
CanMaximize() const609 bool AboutChromeView::CanMaximize() const {
610   return false;
611 }
612 
IsAlwaysOnTop() const613 bool AboutChromeView::IsAlwaysOnTop() const {
614   return false;
615 }
616 
HasAlwaysOnTopMenu() const617 bool AboutChromeView::HasAlwaysOnTopMenu() const {
618   return false;
619 }
620 
IsModal() const621 bool AboutChromeView::IsModal() const {
622   return true;
623 }
624 
Accept()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 
GetContentsView()636 views::View* AboutChromeView::GetContentsView() {
637   return this;
638 }
639 
640 ////////////////////////////////////////////////////////////////////////////////
641 // AboutChromeView, views::LinkController implementation:
642 
LinkActivated(views::Link * source,int event_flags)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)
OnOSVersion(chromeos::VersionLoader::Handle handle,std::string version)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 
OnReportResults(GoogleUpdateUpgradeResult result,GoogleUpdateErrorCode error_code,const std::wstring & version)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 
UpdateStatus(GoogleUpdateUpgradeResult result,GoogleUpdateErrorCode error_code)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