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"
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"
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
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
,
62 return text
.substr(start
, end
- start
);
67 ////////////////////////////////////////////////////////////////////////////////
68 // AboutChromeView, public:
70 AboutChromeView::AboutChromeView(Profile
* profile
)
72 about_dlg_background_logo_(NULL
),
73 about_title_label_(NULL
),
75 copyright_label_(NULL
),
76 main_text_label_(NULL
),
77 main_text_label_height_(0),
78 terms_of_service_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) {
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.
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";
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
")";
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
);
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
),
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() +
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);
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
,
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
;
261 // Use whichever is greater (the calculated height or the specified minimum
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
,
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
,
308 // Then the main_text_label.
309 main_text_label_
->SetBounds(kPanelHorizMargin
,
310 copyright_label_
->y() +
311 copyright_label_
->height(),
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
,
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
361 canvas
->TileImageInt(*kBackgroundBmp
, 0, 0,
362 dialog_dimensions_
.width(), kBackgroundBmp
->height());
365 ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont
);
367 const gfx::Rect label_bounds
= main_text_label_
->bounds();
370 chromium_url_appears_first_
? chromium_url_
: open_source_url_
;
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.
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
,
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
,
401 // The last text chunk doesn't have a URL associated with it.
402 DrawTextAndPositionUrl(canvas
, main_label_chunk5_
, NULL
, NULL
, &position
,
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());
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
,
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))
445 // Iterate over each run and draw it.
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.
469 gfx::Size sz
= link
->GetPreferredSize();
470 WrapIfWordDoesntFit(sz
.width(), font
.height(), position
, bounds
);
471 *rect
= gfx::Rect(position
->width(), position
->height(), sz
.width(),
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
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
,
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
);
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.
507 word
= iter
.GetWord(); // Get the next word.
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()) +
519 if (text_direction_is_rtl_
) {
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
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
);
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());
540 // Otherwise, we advance position to the next word.
541 position
->Enlarge(w
, 0);
545 break; // LTR within RTL is drawn as one unit, so we are done.
549 void AboutChromeView::WrapIfWordDoesntFit(int word_width
,
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
,
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.
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.
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
);
624 bool AboutChromeView::IsDialogButtonEnabled(DialogButton button
) const {
625 if (button
== DIALOGBUTTON_OK
&& check_button_status_
!= CHECKBUTTON_ENABLED
)
631 bool AboutChromeView::IsDialogButtonVisible(DialogButton button
) const {
632 if (button
== DIALOGBUTTON_OK
&& check_button_status_
== CHECKBUTTON_HIDDEN
)
638 bool AboutChromeView::CanResize() const {
642 bool AboutChromeView::CanMaximize() const {
646 bool AboutChromeView::IsAlwaysOnTop() const {
650 bool AboutChromeView::HasAlwaysOnTopMenu() const {
654 bool AboutChromeView::IsModal() const {
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
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() {
680 ////////////////////////////////////////////////////////////////////////////////
681 // AboutChromeView, views::LinkController implementation:
683 void AboutChromeView::LinkActivated(views::Link
* source
,
686 if (source
== terms_of_service_url_
)
688 else if (source
== chromium_url_
)
689 url
= GURL(kChromiumUrl
);
690 else if (source
== open_source_url_
)
691 url
= GURL(kAcknowledgements
);
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
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.
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
));
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
));
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;
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
),
762 show_success_indicator
= true;
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_
);
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());
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;
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
);
805 // We have updated controls on the parent, so we need to update its layout.
806 View
* parent
= GetParent();
809 // Check button may have appeared/disappeared. We cannot call this during
810 // ViewHierarchyChanged because the |window()| pointer hasn't been set yet.
812 GetDialogClientView()->UpdateDialogButtons();