Fix 1955: Show Chromium build number after version (Chromium only)
[chromium-blink-merge.git] / chrome / browser / views / about_chrome_view.cc
blob09a4ccf2346232e4fd2f5584b9baa1c3bae55285
1 // Copyright (c) 2006-2008 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.
5 #include "chrome/browser/views/about_chrome_view.h"
7 #include <commdlg.h>
9 #include "base/file_version_info.h"
10 #include "base/string_util.h"
11 #include "base/win_util.h"
12 #include "base/word_iterator.h"
13 #include "chrome/app/locales/locale_settings.h"
14 #include "chrome/app/theme/theme_resources.h"
15 #include "chrome/browser/browser_list.h"
16 #include "chrome/common/gfx/chrome_canvas.h"
17 #include "chrome/common/gfx/color_utils.h"
18 #include "chrome/browser/user_metrics.h"
19 #include "chrome/browser/views/restart_message_box.h"
20 #include "chrome/browser/views/standard_layout.h"
21 #include "chrome/common/l10n_util.h"
22 #include "chrome/common/resource_bundle.h"
23 #include "chrome/common/chrome_constants.h"
24 #include "chrome/installer/util/install_util.h"
25 #include "chrome/views/text_field.h"
26 #include "chrome/views/throbber.h"
27 #include "chrome/views/window.h"
28 #include "webkit/glue/webkit_glue.h"
30 #include "chromium_strings.h"
31 #include "generated_resources.h"
33 namespace {
34 // The pixel width of the version text field. Ideally, we'd like to have the
35 // bounds set to the edge of the icon. However, the icon is not a view but a
36 // part of the background, so we have to hard code the width to make sure
37 // the version field doesn't overlap it.
38 const int kVersionFieldWidth = 195;
40 // The URLs that you navigate to when clicking the links in the About dialog.
41 const wchar_t* const kChromiumUrl = L"http://www.chromium.org/";
42 const wchar_t* const kAcknowledgements = L"about:credits";
43 const wchar_t* const kTOS = L"about:terms";
45 // These are used as placeholder text around the links in the text in the about
46 // dialog.
47 const wchar_t* kBeginLink = L"BEGIN_LINK";
48 const wchar_t* kEndLink = L"END_LINK";
49 const wchar_t* kBeginLinkChr = L"BEGIN_LINK_CHR";
50 const wchar_t* kBeginLinkOss = L"BEGIN_LINK_OSS";
51 const wchar_t* kEndLinkChr = L"END_LINK_CHR";
52 const wchar_t* kEndLinkOss = L"END_LINK_OSS";
54 // The background bitmap used to draw the background color for the About box
55 // and the separator line (this is the image we will draw the logo on top of).
56 static const SkBitmap* kBackgroundBmp = NULL;
58 // Returns a substring from |text| between start and end.
59 std::wstring StringSubRange(const std::wstring& text, size_t start,
60 size_t end) {
61 DCHECK(end > start);
62 return text.substr(start, end - start);
65 } // namespace
67 ////////////////////////////////////////////////////////////////////////////////
68 // AboutChromeView, public:
70 AboutChromeView::AboutChromeView(Profile* profile)
71 : profile_(profile),
72 about_dlg_background_logo_(NULL),
73 about_title_label_(NULL),
74 version_label_(NULL),
75 copyright_label_(NULL),
76 main_text_label_(NULL),
77 main_text_label_height_(0),
78 terms_of_service_url_(NULL),
79 chromium_url_(NULL),
80 open_source_url_(NULL),
81 chromium_url_appears_first_(true),
82 check_button_status_(CHECKBUTTON_HIDDEN),
83 text_direction_is_rtl_(false) {
84 DCHECK(profile);
85 Init();
87 google_updater_ = new GoogleUpdate();
88 google_updater_->AddStatusChangeListener(this);
90 if (kBackgroundBmp == NULL) {
91 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
92 kBackgroundBmp = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND_COLOR);
96 AboutChromeView::~AboutChromeView() {
97 // The Google Updater will hold a pointer to us until it reports status, so we
98 // need to let it know that we will no longer be listening.
99 if (google_updater_)
100 google_updater_->RemoveStatusChangeListener();
103 void AboutChromeView::Init() {
104 text_direction_is_rtl_ =
105 l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
106 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
108 scoped_ptr<FileVersionInfo> version_info(
109 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
110 if (version_info.get() == NULL) {
111 NOTREACHED() << L"Failed to initialize about window";
112 return;
115 current_version_ = version_info->file_version();
116 #if !defined(GOOGLE_CHROME_BUILD)
117 current_version_ += L" (";
118 current_version_ += version_info->last_change();
119 current_version_ += L")";
120 #endif
122 // Views we will add to the *parent* of this dialog, since it will display
123 // next to the buttons which we don't draw ourselves.
124 throbber_.reset(new views::Throbber(50, true));
125 throbber_->SetParentOwned(false);
126 throbber_->SetVisible(false);
128 SkBitmap* success_image = rb.GetBitmapNamed(IDR_UPDATE_UPTODATE);
129 success_indicator_.SetImage(*success_image);
130 success_indicator_.SetParentOwned(false);
132 SkBitmap* update_available_image = rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE);
133 update_available_indicator_.SetImage(*update_available_image);
134 update_available_indicator_.SetParentOwned(false);
136 SkBitmap* timeout_image = rb.GetBitmapNamed(IDR_UPDATE_FAIL);
137 timeout_indicator_.SetImage(*timeout_image);
138 timeout_indicator_.SetParentOwned(false);
140 update_label_.SetVisible(false);
141 update_label_.SetParentOwned(false);
143 // Regular view controls we draw by ourself. First, we add the background
144 // image for the dialog. We have two different background bitmaps, one for
145 // LTR UIs and one for RTL UIs. We load the correct bitmap based on the UI
146 // layout of the view.
147 about_dlg_background_logo_ = new views::ImageView();
148 SkBitmap* about_background_logo;
149 if (UILayoutIsRightToLeft())
150 about_background_logo = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND_RTL);
151 else
152 about_background_logo = rb.GetBitmapNamed(IDR_ABOUT_BACKGROUND);
154 about_dlg_background_logo_->SetImage(*about_background_logo);
155 AddChildView(about_dlg_background_logo_);
157 // Add the dialog labels.
158 about_title_label_ = new views::Label(
159 l10n_util::GetString(IDS_PRODUCT_NAME));
160 about_title_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
161 ResourceBundle::BaseFont).DeriveFont(18, BOLD_FONTTYPE));
162 AddChildView(about_title_label_);
164 // This is a text field so people can copy the version number from the dialog.
165 version_label_ = new views::TextField();
166 version_label_->SetText(current_version_);
167 version_label_->SetReadOnly(true);
168 version_label_->RemoveBorder();
169 version_label_->SetBackgroundColor(SK_ColorWHITE);
170 version_label_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
171 ResourceBundle::BaseFont).DeriveFont(0, BOLD_FONTTYPE));
172 AddChildView(version_label_);
174 // The copyright URL portion of the main label.
175 copyright_label_ = new views::Label(
176 l10n_util::GetString(IDS_ABOUT_VERSION_COPYRIGHT));
177 copyright_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
178 AddChildView(copyright_label_);
180 main_text_label_ = new views::Label(L"");
182 // Figure out what to write in the main label of the About box.
183 std::wstring text = l10n_util::GetString(IDS_ABOUT_VERSION_LICENSE);
185 chromium_url_appears_first_ =
186 text.find(kBeginLinkChr) < text.find(kBeginLinkOss);
188 size_t link1 = text.find(kBeginLink);
189 DCHECK(link1 != std::wstring::npos);
190 size_t link1_end = text.find(kEndLink, link1);
191 DCHECK(link1_end != std::wstring::npos);
192 size_t link2 = text.find(kBeginLink, link1_end);
193 DCHECK(link2 != std::wstring::npos);
194 size_t link2_end = text.find(kEndLink, link2);
195 DCHECK(link1_end != std::wstring::npos);
197 main_label_chunk1_ = text.substr(0, link1);
198 main_label_chunk2_ = StringSubRange(text, link1_end + wcslen(kEndLinkOss),
199 link2);
200 main_label_chunk3_ = text.substr(link2_end + wcslen(kEndLinkOss));
202 // The Chromium link within the main text of the dialog.
203 chromium_url_ = new views::Link(
204 StringSubRange(text, text.find(kBeginLinkChr) + wcslen(kBeginLinkChr),
205 text.find(kEndLinkChr)));
206 AddChildView(chromium_url_);
207 chromium_url_->SetController(this);
209 // The Open Source link within the main text of the dialog.
210 open_source_url_ = new views::Link(
211 StringSubRange(text, text.find(kBeginLinkOss) + wcslen(kBeginLinkOss),
212 text.find(kEndLinkOss)));
213 AddChildView(open_source_url_);
214 open_source_url_->SetController(this);
216 // Add together all the strings in the dialog for the purpose of calculating
217 // the height of the dialog. The space for the Terms of Service string is not
218 // included (it is added later, if needed).
219 std::wstring full_text = main_label_chunk1_ + chromium_url_->GetText() +
220 main_label_chunk2_ + open_source_url_->GetText() +
221 main_label_chunk3_;
223 dialog_dimensions_ = views::Window::GetLocalizedContentsSize(
224 IDS_ABOUT_DIALOG_WIDTH_CHARS,
225 IDS_ABOUT_DIALOG_MINIMUM_HEIGHT_LINES);
227 // Create a label and add the full text so we can query it for the height.
228 views::Label dummy_text(full_text);
229 dummy_text.SetMultiLine(true);
230 ChromeFont font =
231 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
233 // Add up the height of the various elements on the page.
234 int height = about_background_logo->height() +
235 kRelatedControlVerticalSpacing +
236 font.height() + // Copyright line.
237 dummy_text.GetHeightForWidth( // Main label.
238 dialog_dimensions_.width() - (2 * kPanelHorizMargin)) +
239 kRelatedControlVerticalSpacing;
241 #if defined(GOOGLE_CHROME_BUILD)
242 std::vector<size_t> url_offsets;
243 text = l10n_util::GetStringF(IDS_ABOUT_TERMS_OF_SERVICE,
244 std::wstring(),
245 std::wstring(),
246 &url_offsets);
248 main_label_chunk4_ = text.substr(0, url_offsets[0]);
249 main_label_chunk5_ = text.substr(url_offsets[0]);
251 // The Terms of Service URL at the bottom.
252 terms_of_service_url_ =
253 new views::Link(l10n_util::GetString(IDS_TERMS_OF_SERVICE));
254 AddChildView(terms_of_service_url_);
255 terms_of_service_url_->SetController(this);
257 // Add the Terms of Service line and some whitespace.
258 height += font.height() + kRelatedControlVerticalSpacing;
259 #endif
261 // Use whichever is greater (the calculated height or the specified minimum
262 // height).
263 dialog_dimensions_.set_height(std::max(height, dialog_dimensions_.height()));
266 ////////////////////////////////////////////////////////////////////////////////
267 // AboutChromeView, views::View implementation:
269 gfx::Size AboutChromeView::GetPreferredSize() {
270 return dialog_dimensions_;
273 void AboutChromeView::Layout() {
274 gfx::Size panel_size = GetPreferredSize();
276 // Background image for the dialog.
277 gfx::Size sz = about_dlg_background_logo_->GetPreferredSize();
278 // Used to position main text below.
279 int background_image_height = sz.height();
280 about_dlg_background_logo_->SetBounds(panel_size.width() - sz.width(), 0,
281 sz.width(), sz.height());
283 // First label goes to the top left corner.
284 sz = about_title_label_->GetPreferredSize();
285 about_title_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
286 sz.width(), sz.height());
288 // Then we have the version number right below it.
289 sz = version_label_->GetPreferredSize();
290 version_label_->SetBounds(kPanelHorizMargin,
291 about_title_label_->y() +
292 about_title_label_->height() +
293 kRelatedControlVerticalSpacing,
294 kVersionFieldWidth,
295 sz.height());
297 // For the width of the main text label we want to use up the whole panel
298 // width and remaining height, minus a little margin on each side.
299 int y_pos = background_image_height + kRelatedControlVerticalSpacing;
300 sz.set_width(panel_size.width() - 2 * kPanelHorizMargin);
302 // Draw the text right below the background image.
303 copyright_label_->SetBounds(kPanelHorizMargin,
304 y_pos,
305 sz.width(),
306 sz.height());
308 // Then the main_text_label.
309 main_text_label_->SetBounds(kPanelHorizMargin,
310 copyright_label_->y() +
311 copyright_label_->height(),
312 sz.width(),
313 main_text_label_height_);
315 // Get the y-coordinate of our parent so we can position the text left of the
316 // buttons at the bottom.
317 gfx::Rect parent_bounds = GetParent()->GetLocalBounds(false);
319 sz = throbber_->GetPreferredSize();
320 int throbber_topleft_x = kPanelHorizMargin;
321 int throbber_topleft_y = parent_bounds.bottom() - sz.height() -
322 kButtonVEdgeMargin - 3;
323 throbber_->SetBounds(throbber_topleft_x, throbber_topleft_y,
324 sz.width(), sz.height());
326 // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
327 sz = success_indicator_.GetPreferredSize();
328 success_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
329 sz.width(), sz.height());
331 // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
332 sz = update_available_indicator_.GetPreferredSize();
333 update_available_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
334 sz.width(), sz.height());
336 // This image is hidden (see ViewHierarchyChanged) and displayed on demand.
337 sz = timeout_indicator_.GetPreferredSize();
338 timeout_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y,
339 sz.width(), sz.height());
341 // The update label should be at the bottom of the screen, to the right of
342 // the throbber. We specify width to the end of the dialog because it contains
343 // variable length messages.
344 sz = update_label_.GetPreferredSize();
345 int update_label_x = throbber_->x() + throbber_->width() +
346 kRelatedControlHorizontalSpacing;
347 update_label_.SetHorizontalAlignment(views::Label::ALIGN_LEFT);
348 update_label_.SetBounds(update_label_x,
349 throbber_topleft_y + 1,
350 parent_bounds.width() - update_label_x,
351 sz.height());
355 void AboutChromeView::Paint(ChromeCanvas* canvas) {
356 views::View::Paint(canvas);
358 // Draw the background image color (and the separator) across the dialog.
359 // This will become the background for the logo image at the top of the
360 // dialog.
361 canvas->TileImageInt(*kBackgroundBmp, 0, 0,
362 dialog_dimensions_.width(), kBackgroundBmp->height());
364 ChromeFont font =
365 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
367 const gfx::Rect label_bounds = main_text_label_->bounds();
369 views::Link* link1 =
370 chromium_url_appears_first_ ? chromium_url_ : open_source_url_;
371 views::Link* link2 =
372 chromium_url_appears_first_ ? open_source_url_ : chromium_url_;
373 gfx::Rect* rect1 = chromium_url_appears_first_ ?
374 &chromium_url_rect_ : &open_source_url_rect_;
375 gfx::Rect* rect2 = chromium_url_appears_first_ ?
376 &open_source_url_rect_ : &chromium_url_rect_;
378 // This struct keeps track of where to write the next word (which x,y
379 // pixel coordinate). This struct is updated after drawing text and checking
380 // if we need to wrap.
381 gfx::Size position;
382 // Draw the first text chunk and position the Chromium url.
383 DrawTextAndPositionUrl(canvas, main_label_chunk1_, link1,
384 rect1, &position, label_bounds, font);
385 // Draw the second text chunk and position the Open Source url.
386 DrawTextAndPositionUrl(canvas, main_label_chunk2_, link2,
387 rect2, &position, label_bounds, font);
388 // Draw the third text chunk (which has no URL associated with it).
389 DrawTextAndPositionUrl(canvas, main_label_chunk3_, NULL, NULL, &position,
390 label_bounds, font);
392 #if defined(GOOGLE_CHROME_BUILD)
393 // Insert a line break and some whitespace.
394 position.set_width(0);
395 position.Enlarge(0, font.height() + kRelatedControlVerticalSpacing);
397 // And now the Terms of Service and position the TOS url.
398 DrawTextAndPositionUrl(canvas, main_label_chunk4_, terms_of_service_url_,
399 &terms_of_service_url_rect_, &position, label_bounds,
400 font);
401 // The last text chunk doesn't have a URL associated with it.
402 DrawTextAndPositionUrl(canvas, main_label_chunk5_, NULL, NULL, &position,
403 label_bounds, font);
405 // Position the TOS URL within the main label.
406 terms_of_service_url_->SetBounds(terms_of_service_url_rect_.x(),
407 terms_of_service_url_rect_.y(),
408 terms_of_service_url_rect_.width(),
409 terms_of_service_url_rect_.height());
410 #endif
412 // Position the URLs within the main label. First position the Chromium URL
413 // within the main label.
414 chromium_url_->SetBounds(chromium_url_rect_.x(),
415 chromium_url_rect_.y(),
416 chromium_url_rect_.width(),
417 chromium_url_rect_.height());
418 // Then position the Open Source URL within the main label.
419 open_source_url_->SetBounds(open_source_url_rect_.x(),
420 open_source_url_rect_.y(),
421 open_source_url_rect_.width(),
422 open_source_url_rect_.height());
424 // Save the height so we can set the bounds correctly.
425 main_text_label_height_ = position.height() + font.height();
428 void AboutChromeView::DrawTextAndPositionUrl(ChromeCanvas* canvas,
429 const std::wstring& text,
430 views::Link* link,
431 gfx::Rect* rect,
432 gfx::Size* position,
433 const gfx::Rect& bounds,
434 const ChromeFont& font) {
435 DCHECK(canvas && position);
437 // What we get passed in as |text| is potentially a mix of LTR and RTL "runs"
438 // (a run is a sequence of words that share the same directionality). We
439 // initialize a bidirectional ICU line iterator and split the text into runs
440 // that are either strictly LTR or strictly RTL (and do not contain a mix).
441 l10n_util::BiDiLineIterator bidi_line;
442 if (!bidi_line.Open(text.c_str(), true, false))
443 return;
445 // Iterate over each run and draw it.
446 int run_start = 0;
447 int run_end = 0;
448 const int runs = bidi_line.CountRuns();
449 for (int run = 0; run < runs; ++run) {
450 UBiDiLevel level = 0;
451 bidi_line.GetLogicalRun(run_start, &run_end, &level);
452 std::wstring fragment = StringSubRange(text, run_start, run_end);
454 // A flag that tells us whether we found LTR text inside RTL text.
455 bool ltr_inside_rtl_text =
456 ((level & 1) == UBIDI_LTR) && text_direction_is_rtl_;
458 // Draw the text chunk contained in |fragment|. |position| is relative to
459 // the top left corner of the label we draw inside (also when drawing RTL).
460 DrawTextStartingFrom(canvas, fragment, position, bounds, font,
461 ltr_inside_rtl_text);
463 run_start = run_end; // Advance over what we just drew.
466 // If the caller is interested in placing a link after this text blurb, we
467 // figure out here where to place it.
468 if (link && rect) {
469 gfx::Size sz = link->GetPreferredSize();
470 WrapIfWordDoesntFit(sz.width(), font.height(), position, bounds);
471 *rect = gfx::Rect(position->width(), position->height(), sz.width(),
472 sz.height());
474 // Go from relative pixel coordinates (within the label we are drawing on)
475 // to absolute pixel coordinates (relative to the top left corner of the
476 // dialog content).
477 rect->Offset(bounds.x(), bounds.y());
478 // And leave some space to draw the link in.
479 position->Enlarge(sz.width(), 0);
483 void AboutChromeView::DrawTextStartingFrom(ChromeCanvas* canvas,
484 const std::wstring& text,
485 gfx::Size* position,
486 const gfx::Rect& bounds,
487 const ChromeFont& font,
488 bool ltr_within_rtl) {
489 // Iterate through line breaking opportunities (which in English would be
490 // spaces and such. This tells us where to wrap.
491 WordIterator iter(text, WordIterator::BREAK_LINE);
492 if (!iter.Init())
493 return;
495 int flags = (text_direction_is_rtl_ ?
496 ChromeCanvas::TEXT_ALIGN_RIGHT :
497 ChromeCanvas::TEXT_ALIGN_LEFT) |
498 ChromeCanvas::MULTI_LINE |
499 ChromeCanvas::HIDE_PREFIX;
501 // Iterate over each word in the text, or put in a more locale-neutral way:
502 // iterate to the next line breaking opportunity.
503 while (iter.Advance()) {
504 // Get the word and figure out the dimensions.
505 std::wstring word;
506 if (!ltr_within_rtl)
507 word = iter.GetWord(); // Get the next word.
508 else
509 word = text; // Draw the whole text at once.
511 int w = font.GetStringWidth(word), h = font.height();
512 canvas->SizeStringInt(word, font, &w, &h, flags);
514 // If we exceed the boundaries, we need to wrap.
515 WrapIfWordDoesntFit(w, font.height(), position, bounds);
517 int x = main_text_label_->MirroredXCoordinateInsideView(position->width()) +
518 bounds.x();
519 if (text_direction_is_rtl_) {
520 x -= w;
521 // When drawing LTR strings inside RTL text we need to make sure we draw
522 // the trailing space (if one exists after the LTR text) on the left of
523 // the LTR string.
524 if (ltr_within_rtl && word[word.size() - 1] == L' ') {
525 int space_w = font.GetStringWidth(L" "), space_h = font.height();
526 canvas->SizeStringInt(L" ", font, &space_w, &space_h, flags);
527 x += space_w;
530 int y = position->height() + bounds.y();
532 // Draw the text on the screen (mirrored, if RTL run).
533 canvas->DrawStringInt(word, font, SK_ColorBLACK, x, y, w, h, flags);
535 if (word.size() > 0 && word[word.size() - 1] == L'\x0a') {
536 // When we come across '\n', we move to the beginning of the next line.
537 position->set_width(0);
538 position->Enlarge(0, font.height());
539 } else {
540 // Otherwise, we advance position to the next word.
541 position->Enlarge(w, 0);
544 if (ltr_within_rtl)
545 break; // LTR within RTL is drawn as one unit, so we are done.
549 void AboutChromeView::WrapIfWordDoesntFit(int word_width,
550 int font_height,
551 gfx::Size* position,
552 const gfx::Rect& bounds) {
553 if (position->width() + word_width > bounds.right()) {
554 position->set_width(0);
555 position->Enlarge(0, font_height);
559 void AboutChromeView::ViewHierarchyChanged(bool is_add,
560 views::View* parent,
561 views::View* child) {
562 // Since we want the some of the controls to show up in the same visual row
563 // as the buttons, which are provided by the framework, we must add the
564 // buttons to the non-client view, which is the parent of this view.
565 // Similarly, when we're removed from the view hierarchy, we must take care
566 // to remove these items as well.
567 if (child == this) {
568 if (is_add) {
569 parent->AddChildView(&update_label_);
570 parent->AddChildView(throbber_.get());
571 parent->AddChildView(&success_indicator_);
572 success_indicator_.SetVisible(false);
573 parent->AddChildView(&update_available_indicator_);
574 update_available_indicator_.SetVisible(false);
575 parent->AddChildView(&timeout_indicator_);
576 timeout_indicator_.SetVisible(false);
578 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned
579 // off. So, in this case we just want the About box to not mention
580 // on-demand updates. Silent updates (in the background) should still
581 // work as before - enabling UAC or installing the latest service pack
582 // for Vista is another option.
583 int service_pack_major = 0, service_pack_minor = 0;
584 win_util::GetServicePackLevel(&service_pack_major, &service_pack_minor);
585 if (win_util::UserAccountControlIsEnabled() ||
586 win_util::GetWinVersion() == win_util::WINVERSION_XP ||
587 (win_util::GetWinVersion() == win_util::WINVERSION_VISTA &&
588 service_pack_major >= 1)) {
589 UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR);
590 google_updater_->CheckForUpdate(false); // false=don't upgrade yet.
592 } else {
593 parent->RemoveChildView(&update_label_);
594 parent->RemoveChildView(throbber_.get());
595 parent->RemoveChildView(&success_indicator_);
596 parent->RemoveChildView(&update_available_indicator_);
597 parent->RemoveChildView(&timeout_indicator_);
602 ////////////////////////////////////////////////////////////////////////////////
603 // AboutChromeView, views::DialogDelegate implementation:
605 int AboutChromeView::GetDialogButtons() const {
606 return DIALOGBUTTON_OK | DIALOGBUTTON_CANCEL;
609 std::wstring AboutChromeView::GetDialogButtonLabel(
610 DialogButton button) const {
611 if (button == DIALOGBUTTON_OK) {
612 return l10n_util::GetString(IDS_ABOUT_CHROME_UPDATE_CHECK);
613 } else if (button == DIALOGBUTTON_CANCEL) {
614 // The OK button (which is the default button) has been re-purposed to be
615 // 'Check for Updates' so we want the Cancel button should have the label
616 // OK but act like a Cancel button in all other ways.
617 return l10n_util::GetString(IDS_OK);
620 NOTREACHED();
621 return L"";
624 bool AboutChromeView::IsDialogButtonEnabled(DialogButton button) const {
625 if (button == DIALOGBUTTON_OK && check_button_status_ != CHECKBUTTON_ENABLED)
626 return false;
628 return true;
631 bool AboutChromeView::IsDialogButtonVisible(DialogButton button) const {
632 if (button == DIALOGBUTTON_OK && check_button_status_ == CHECKBUTTON_HIDDEN)
633 return false;
635 return true;
638 bool AboutChromeView::CanResize() const {
639 return false;
642 bool AboutChromeView::CanMaximize() const {
643 return false;
646 bool AboutChromeView::IsAlwaysOnTop() const {
647 return false;
650 bool AboutChromeView::HasAlwaysOnTopMenu() const {
651 return false;
654 bool AboutChromeView::IsModal() const {
655 return true;
658 std::wstring AboutChromeView::GetWindowTitle() const {
659 return l10n_util::GetString(IDS_ABOUT_CHROME_TITLE);
662 bool AboutChromeView::Accept() {
663 UpdateStatus(UPGRADE_STARTED, GOOGLE_UPDATE_NO_ERROR);
665 // The Upgrade button isn't available until we have received notification
666 // that an update is available, at which point this pointer should have been
667 // null-ed out.
668 DCHECK(!google_updater_);
669 google_updater_ = new GoogleUpdate();
670 google_updater_->AddStatusChangeListener(this);
671 google_updater_->CheckForUpdate(true); // true=upgrade if new version found.
673 return false; // We never allow this button to close the window.
676 views::View* AboutChromeView::GetContentsView() {
677 return this;
680 ////////////////////////////////////////////////////////////////////////////////
681 // AboutChromeView, views::LinkController implementation:
683 void AboutChromeView::LinkActivated(views::Link* source,
684 int event_flags) {
685 GURL url;
686 if (source == terms_of_service_url_)
687 url = GURL(kTOS);
688 else if (source == chromium_url_)
689 url = GURL(kChromiumUrl);
690 else if (source == open_source_url_)
691 url = GURL(kAcknowledgements);
692 else
693 NOTREACHED() << "Unknown link source";
695 Browser* browser = BrowserList::GetLastActive();
696 browser->OpenURL(url, GURL(), NEW_WINDOW, PageTransition::LINK);
699 ////////////////////////////////////////////////////////////////////////////////
700 // AboutChromeView, GoogleUpdateStatusListener implementation:
702 void AboutChromeView::OnReportResults(GoogleUpdateUpgradeResult result,
703 GoogleUpdateErrorCode error_code,
704 const std::wstring& version) {
705 // Drop the last reference to the object so that it gets cleaned up here.
706 google_updater_ = NULL;
708 // Make a note of which version Google Update is reporting is the latest
709 // version.
710 new_version_available_ = version;
711 UpdateStatus(result, error_code);
714 ////////////////////////////////////////////////////////////////////////////////
715 // AboutChromeView, private:
717 void AboutChromeView::UpdateStatus(GoogleUpdateUpgradeResult result,
718 GoogleUpdateErrorCode error_code) {
719 bool show_success_indicator = false;
720 bool show_update_available_indicator = false;
721 bool show_timeout_indicator = false;
722 bool show_throbber = false;
723 bool show_update_label = true; // Always visible, except at start.
725 switch (result) {
726 case UPGRADE_STARTED:
727 UserMetrics::RecordAction(L"Upgrade_Started", profile_);
728 check_button_status_ = CHECKBUTTON_DISABLED;
729 show_throbber = true;
730 update_label_.SetText(l10n_util::GetString(IDS_UPGRADE_STARTED));
731 break;
732 case UPGRADE_CHECK_STARTED:
733 UserMetrics::RecordAction(L"UpgradeCheck_Started", profile_);
734 check_button_status_ = CHECKBUTTON_HIDDEN;
735 show_throbber = true;
736 update_label_.SetText(l10n_util::GetString(IDS_UPGRADE_CHECK_STARTED));
737 break;
738 case UPGRADE_IS_AVAILABLE:
739 UserMetrics::RecordAction(L"UpgradeCheck_UpgradeIsAvailable", profile_);
740 check_button_status_ = CHECKBUTTON_ENABLED;
741 update_label_.SetText(
742 l10n_util::GetStringF(IDS_UPGRADE_AVAILABLE,
743 l10n_util::GetString(IDS_PRODUCT_NAME)));
744 show_update_available_indicator = true;
745 break;
746 case UPGRADE_ALREADY_UP_TO_DATE: {
747 // Google Update reported that Chrome is up-to-date. Now make sure that we
748 // are running the latest version and if not, notify the user by falling
749 // into the next case of UPGRADE_SUCCESSFUL.
750 scoped_ptr<installer::Version> installed_version(
751 InstallUtil::GetChromeVersion(false));
752 scoped_ptr<installer::Version> running_version(
753 installer::Version::GetVersionFromString(current_version_));
754 if (!installed_version.get() ||
755 !installed_version->IsHigherThan(running_version.get())) {
756 UserMetrics::RecordAction(L"UpgradeCheck_AlreadyUpToDate", profile_);
757 check_button_status_ = CHECKBUTTON_HIDDEN;
758 update_label_.SetText(
759 l10n_util::GetStringF(IDS_UPGRADE_ALREADY_UP_TO_DATE,
760 l10n_util::GetString(IDS_PRODUCT_NAME),
761 current_version_));
762 show_success_indicator = true;
763 break;
765 // No break here as we want to notify user about upgrade if there is one.
767 case UPGRADE_SUCCESSFUL: {
768 if (result == UPGRADE_ALREADY_UP_TO_DATE)
769 UserMetrics::RecordAction(L"UpgradeCheck_AlreadyUpgraded", profile_);
770 else
771 UserMetrics::RecordAction(L"UpgradeCheck_Upgraded", profile_);
772 check_button_status_ = CHECKBUTTON_HIDDEN;
773 const std::wstring& update_string = new_version_available_.empty()
774 ? l10n_util::GetStringF(IDS_UPGRADE_SUCCESSFUL_NOVERSION,
775 l10n_util::GetString(IDS_PRODUCT_NAME))
776 : l10n_util::GetStringF(IDS_UPGRADE_SUCCESSFUL,
777 l10n_util::GetString(IDS_PRODUCT_NAME),
778 new_version_available_);
779 update_label_.SetText(update_string);
780 show_success_indicator = true;
781 RestartMessageBox::ShowMessageBox(window()->GetHWND());
782 break;
784 case UPGRADE_ERROR:
785 UserMetrics::RecordAction(L"UpgradeCheck_Error", profile_);
786 check_button_status_ = CHECKBUTTON_HIDDEN;
787 update_label_.SetText(l10n_util::GetStringF(IDS_UPGRADE_ERROR,
788 IntToWString(error_code)));
789 show_timeout_indicator = true;
790 break;
791 default:
792 NOTREACHED();
795 success_indicator_.SetVisible(show_success_indicator);
796 update_available_indicator_.SetVisible(show_update_available_indicator);
797 timeout_indicator_.SetVisible(show_timeout_indicator);
798 update_label_.SetVisible(show_update_label);
799 throbber_->SetVisible(show_throbber);
800 if (show_throbber)
801 throbber_->Start();
802 else
803 throbber_->Stop();
805 // We have updated controls on the parent, so we need to update its layout.
806 View* parent = GetParent();
807 parent->Layout();
809 // Check button may have appeared/disappeared. We cannot call this during
810 // ViewHierarchyChanged because the |window()| pointer hasn't been set yet.
811 if (window())
812 GetDialogClientView()->UpdateDialogButtons();