OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 <vector> | |
13 | |
14 #include "base/callback.h" | |
15 #include "base/i18n/rtl.h" | |
16 #include "base/string_number_conversions.h" | |
17 #include "base/threading/thread_restrictions.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/lifetime/application_lifetime.h" | |
22 #include "chrome/browser/prefs/pref_service.h" | |
23 #include "chrome/browser/ui/browser.h" | |
24 #include "chrome/common/chrome_constants.h" | |
25 #include "chrome/common/chrome_version_info.h" | |
26 #include "chrome/common/url_constants.h" | |
27 #include "chrome/installer/util/browser_distribution.h" | |
28 #include "content/public/browser/user_metrics.h" | |
29 #include "grit/chromium_strings.h" | |
30 #include "grit/generated_resources.h" | |
31 #include "grit/locale_settings.h" | |
32 #include "grit/theme_resources.h" | |
33 #include "ui/base/l10n/l10n_util.h" | |
34 #include "ui/base/resource/resource_bundle.h" | |
35 #include "ui/gfx/canvas.h" | |
36 #include "ui/gfx/color_utils.h" | |
37 #include "ui/views/controls/button/text_button.h" | |
38 #include "ui/views/controls/link.h" | |
39 #include "ui/views/controls/textfield/textfield.h" | |
40 #include "ui/views/controls/throbber.h" | |
41 #include "ui/views/layout/layout_constants.h" | |
42 #include "ui/views/view_text_utils.h" | |
43 #include "ui/views/widget/widget.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/browser_process.h" | |
49 #include "chrome/installer/util/install_util.h" | |
50 #endif // defined(OS_WIN) | |
51 | |
52 using content::OpenURLParams; | |
53 using content::Referrer; | |
54 using content::UserMetricsAction; | |
55 | |
56 // The amount of vertical space separating the error label at the bottom from | |
57 // the rest of the text. | |
58 static const int kErrorLabelVerticalSpacing = 15; // Pixels. | |
59 | |
60 namespace { | |
61 // These are used as placeholder text around the links in the text in the about | |
62 // dialog. | |
63 string16 kBeginLink() { | |
64 return ASCIIToUTF16("BEGIN_LINK"); | |
65 } | |
66 string16 kEndLink() { | |
67 return ASCIIToUTF16("END_LINK"); | |
68 } | |
69 string16 kBeginLinkChr() { | |
70 return ASCIIToUTF16("BEGIN_LINK_CHR"); | |
71 } | |
72 string16 kBeginLinkOss() { | |
73 return ASCIIToUTF16("BEGIN_LINK_OSS"); | |
74 } | |
75 string16 kEndLinkChr() { | |
76 return ASCIIToUTF16("END_LINK_CHR"); | |
77 } | |
78 string16 kEndLinkOss() { | |
79 return ASCIIToUTF16("END_LINK_OSS"); | |
80 } | |
81 | |
82 // Returns a substring from |text| between start and end. | |
83 string16 StringSubRange(const string16& text, size_t start, size_t end) { | |
84 DCHECK(end > start); | |
85 return text.substr(start, end - start); | |
86 } | |
87 | |
88 } // namespace | |
89 | |
90 namespace chrome { | |
91 | |
92 // Declared in browser_dialogs.h so that others don't | |
93 // need to depend on our .h. | |
94 void ShowAboutChromeView(gfx::NativeWindow parent, Browser* browser) { | |
95 views::Widget* window = views::Widget::CreateWindowWithParent( | |
96 new AboutChromeView(browser), parent); | |
97 window->Show(); | |
98 } | |
99 | |
100 } // namespace chrome | |
101 | |
102 //////////////////////////////////////////////////////////////////////////////// | |
103 // AboutChromeView, public: | |
104 | |
105 AboutChromeView::AboutChromeView(Browser* browser) | |
106 : browser_(browser), | |
107 about_dlg_background_logo_(NULL), | |
108 about_title_label_(NULL), | |
109 version_label_(NULL), | |
110 copyright_label_(NULL), | |
111 main_text_label_(NULL), | |
112 main_text_label_height_(0), | |
113 chromium_url_(NULL), | |
114 open_source_url_(NULL), | |
115 terms_of_service_url_(NULL), | |
116 error_label_(NULL), | |
117 restart_button_visible_(false), | |
118 chromium_url_appears_first_(true), | |
119 text_direction_is_rtl_(false) { | |
120 DCHECK(browser); | |
121 | |
122 Init(); | |
123 | |
124 #if defined(OS_WIN) && !defined(USE_AURA) | |
125 google_updater_ = new GoogleUpdate(); | |
126 google_updater_->set_status_listener(this); | |
127 #endif | |
128 } | |
129 | |
130 AboutChromeView::~AboutChromeView() { | |
131 #if defined(OS_WIN) && !defined(USE_AURA) | |
132 // The Google Updater will hold a pointer to us until it reports status, so we | |
133 // need to let it know that we will no longer be listening. | |
134 if (google_updater_) | |
135 google_updater_->set_status_listener(NULL); | |
136 #endif | |
137 } | |
138 | |
139 void AboutChromeView::Init() { | |
140 text_direction_is_rtl_ = base::i18n::IsRTL(); | |
141 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
142 | |
143 // Views we will add to the *parent* of this dialog, since it will display | |
144 // next to the buttons which we don't draw ourselves. | |
145 throbber_.reset(new views::Throbber(50, true)); | |
146 throbber_->set_owned_by_client(); | |
147 throbber_->SetVisible(false); | |
148 | |
149 gfx::ImageSkia* success_image = rb.GetImageSkiaNamed(IDR_UPDATE_UPTODATE); | |
150 success_indicator_.SetImage(*success_image); | |
151 success_indicator_.set_owned_by_client(); | |
152 | |
153 gfx::ImageSkia* update_available_image = rb.GetImageSkiaNamed( | |
154 IDR_UPDATE_AVAILABLE); | |
155 update_available_indicator_.SetImage(*update_available_image); | |
156 update_available_indicator_.set_owned_by_client(); | |
157 | |
158 gfx::ImageSkia* timeout_image = rb.GetImageSkiaNamed(IDR_UPDATE_FAIL); | |
159 timeout_indicator_.SetImage(*timeout_image); | |
160 timeout_indicator_.set_owned_by_client(); | |
161 | |
162 update_label_.SetVisible(false); | |
163 update_label_.set_owned_by_client(); | |
164 | |
165 // Regular view controls we draw by ourself. First, we add the background | |
166 // image for the dialog. We have two different background images, one for | |
167 // LTR UIs and one for RTL UIs. We load the correct image based on the UI | |
168 // layout of the view. | |
169 about_dlg_background_logo_ = new views::ImageView(); | |
170 gfx::ImageSkia* about_background_logo = rb.GetImageSkiaNamed( | |
171 base::i18n::IsRTL() ? IDR_ABOUT_BACKGROUND_RTL : IDR_ABOUT_BACKGROUND); | |
172 | |
173 about_dlg_background_logo_->SetImage(*about_background_logo); | |
174 AddChildView(about_dlg_background_logo_); | |
175 | |
176 // Add the dialog labels. | |
177 about_title_label_ = new views::Label( | |
178 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); | |
179 about_title_label_->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont( | |
180 ui::ResourceBundle::BaseFont).DeriveFont(18)); | |
181 about_title_label_->SetBackgroundColor(SK_ColorWHITE); | |
182 about_title_label_->SetEnabledColor(SK_ColorBLACK); | |
183 AddChildView(about_title_label_); | |
184 | |
185 // This is a text field so people can copy the version number from the dialog. | |
186 version_label_ = new views::Textfield(); | |
187 chrome::VersionInfo version_info; | |
188 { | |
189 // TODO(finnur): Need to evaluate whether we should be doing IO here. | |
190 // See issue: http://crbug.com/101699. | |
191 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
192 version_label_->SetText(UTF8ToUTF16(version_info.CreateVersionString())); | |
193 } | |
194 version_label_->SetReadOnly(true); | |
195 version_label_->RemoveBorder(); | |
196 version_label_->SetTextColor(SK_ColorBLACK); | |
197 version_label_->SetBackgroundColor(SK_ColorWHITE); | |
198 version_label_->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont( | |
199 ui::ResourceBundle::BaseFont)); | |
200 AddChildView(version_label_); | |
201 | |
202 // The copyright URL portion of the main label. | |
203 copyright_label_ = new views::Label( | |
204 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT)); | |
205 copyright_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
206 AddChildView(copyright_label_); | |
207 | |
208 main_text_label_ = new views::Label(string16()); | |
209 | |
210 // Figure out what to write in the main label of the About box. | |
211 string16 text = l10n_util::GetStringUTF16(IDS_ABOUT_OLD_VERSION_LICENSE); | |
212 | |
213 chromium_url_appears_first_ = | |
214 text.find(kBeginLinkChr()) < text.find(kBeginLinkOss()); | |
215 | |
216 size_t link1 = text.find(kBeginLink()); | |
217 DCHECK(link1 != string16::npos); | |
218 size_t link1_end = text.find(kEndLink(), link1); | |
219 DCHECK(link1_end != string16::npos); | |
220 size_t link2 = text.find(kBeginLink(), link1_end); | |
221 DCHECK(link2 != string16::npos); | |
222 size_t link2_end = text.find(kEndLink(), link2); | |
223 DCHECK(link1_end != string16::npos); | |
224 | |
225 main_label_chunk1_ = text.substr(0, link1); | |
226 main_label_chunk2_ = StringSubRange(text, link1_end + kEndLinkOss().size(), | |
227 link2); | |
228 main_label_chunk3_ = text.substr(link2_end + kEndLinkOss().size()); | |
229 | |
230 // The Chromium link within the main text of the dialog. | |
231 chromium_url_ = new views::Link( | |
232 StringSubRange(text, text.find(kBeginLinkChr()) + kBeginLinkChr().size(), | |
233 text.find(kEndLinkChr()))); | |
234 AddChildView(chromium_url_); | |
235 chromium_url_->set_listener(this); | |
236 | |
237 // The Open Source link within the main text of the dialog. | |
238 open_source_url_ = new views::Link( | |
239 StringSubRange(text, text.find(kBeginLinkOss()) + kBeginLinkOss().size(), | |
240 text.find(kEndLinkOss()))); | |
241 AddChildView(open_source_url_); | |
242 open_source_url_->set_listener(this); | |
243 | |
244 #if defined(OS_WIN) | |
245 SkColor background_color = color_utils::GetSysSkColor(COLOR_3DFACE); | |
246 copyright_label_->SetBackgroundColor(background_color); | |
247 main_text_label_->SetBackgroundColor(background_color); | |
248 chromium_url_->SetBackgroundColor(background_color); | |
249 open_source_url_->SetBackgroundColor(background_color); | |
250 #endif | |
251 | |
252 // Add together all the strings in the dialog for the purpose of calculating | |
253 // the height of the dialog. The space for the Terms of Service string is not | |
254 // included (it is added later, if needed). | |
255 string16 full_text = main_label_chunk1_ + chromium_url_->text() + | |
256 main_label_chunk2_ + open_source_url_->text() + | |
257 main_label_chunk3_; | |
258 | |
259 dialog_dimensions_ = views::Widget::GetLocalizedContentsSize( | |
260 IDS_ABOUT_DIALOG_WIDTH_CHARS, | |
261 IDS_ABOUT_DIALOG_MINIMUM_HEIGHT_LINES); | |
262 | |
263 // Create a label and add the full text so we can query it for the height. | |
264 views::Label dummy_text(full_text); | |
265 dummy_text.SetMultiLine(true); | |
266 gfx::Font font = ui::ResourceBundle::GetSharedInstance().GetFont( | |
267 ui::ResourceBundle::BaseFont); | |
268 | |
269 // Add up the height of the various elements on the page. | |
270 int height = about_background_logo->height() + | |
271 views::kRelatedControlVerticalSpacing + | |
272 // Copyright line. | |
273 font.GetHeight() + | |
274 // Main label. | |
275 dummy_text.GetHeightForWidth( | |
276 dialog_dimensions_.width() - (2 * views::kPanelHorizMargin)) + | |
277 views::kRelatedControlVerticalSpacing; | |
278 | |
279 #if defined(GOOGLE_CHROME_BUILD) | |
280 std::vector<size_t> url_offsets; | |
281 text = l10n_util::GetStringFUTF16(IDS_ABOUT_OLD_TERMS_OF_SERVICE, | |
282 string16(), | |
283 string16(), | |
284 &url_offsets); | |
285 | |
286 main_label_chunk4_ = text.substr(0, url_offsets[0]); | |
287 main_label_chunk5_ = text.substr(url_offsets[0]); | |
288 | |
289 // The Terms of Service URL at the bottom. | |
290 terms_of_service_url_ = new views::Link( | |
291 l10n_util::GetStringUTF16(IDS_TERMS_OF_SERVICE)); | |
292 AddChildView(terms_of_service_url_); | |
293 terms_of_service_url_->set_listener(this); | |
294 | |
295 error_label_ = new views::Label(); | |
296 error_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
297 error_label_->SetMultiLine(true); | |
298 AddChildView(error_label_); | |
299 | |
300 // Add the Terms of Service line and some whitespace. | |
301 height += font.GetHeight() + views::kRelatedControlVerticalSpacing; | |
302 #endif | |
303 | |
304 // Use whichever is greater (the calculated height or the specified minimum | |
305 // height). | |
306 dialog_dimensions_.set_height(std::max(height, dialog_dimensions_.height())); | |
307 } | |
308 | |
309 //////////////////////////////////////////////////////////////////////////////// | |
310 // AboutChromeView, views::View implementation: | |
311 | |
312 gfx::Size AboutChromeView::GetPreferredSize() { | |
313 return dialog_dimensions_; | |
314 } | |
315 | |
316 void AboutChromeView::Layout() { | |
317 gfx::Size panel_size = GetPreferredSize(); | |
318 | |
319 // Background image for the dialog. | |
320 gfx::Size sz = about_dlg_background_logo_->GetPreferredSize(); | |
321 // Used to position main text below. | |
322 int background_image_height = sz.height(); | |
323 about_dlg_background_logo_->SetBounds(panel_size.width() - sz.width(), 0, | |
324 sz.width(), sz.height()); | |
325 | |
326 // First label goes to the top left corner. | |
327 sz = about_title_label_->GetPreferredSize(); | |
328 about_title_label_->SetBounds( | |
329 views::kPanelHorizMargin, views::kPanelVertMargin, | |
330 sz.width(), sz.height()); | |
331 | |
332 // Then we have the version number right below it. | |
333 sz = version_label_->GetPreferredSize(); | |
334 version_label_->SetBounds(views::kPanelHorizMargin, | |
335 about_title_label_->y() + | |
336 about_title_label_->height() + | |
337 views::kRelatedControlVerticalSpacing, | |
338 panel_size.width() - | |
339 about_dlg_background_logo_->width(), | |
340 sz.height()); | |
341 | |
342 // For the width of the main text label we want to use up the whole panel | |
343 // width and remaining height, minus a little margin on each side. | |
344 int y_pos = background_image_height + views::kRelatedControlVerticalSpacing; | |
345 sz.set_width(panel_size.width() - 2 * views::kPanelHorizMargin); | |
346 | |
347 // Draw the text right below the background image. | |
348 copyright_label_->SetBounds(views::kPanelHorizMargin, | |
349 y_pos, | |
350 sz.width(), | |
351 sz.height()); | |
352 | |
353 // Then the main_text_label. | |
354 main_text_label_->SetBounds(views::kPanelHorizMargin, | |
355 copyright_label_->y() + | |
356 copyright_label_->height(), | |
357 sz.width(), | |
358 main_text_label_height_); | |
359 | |
360 // And the error label at the bottom of the main content. This does not fit on | |
361 // screen until EnlargeWindowSizeIfNeeded has been called (which happens when | |
362 // an error is returned from Google Update). | |
363 if (error_label_) { | |
364 sz.set_height(error_label_->GetHeightForWidth(sz.width())); | |
365 error_label_->SetBounds(main_text_label_->bounds().x(), | |
366 main_text_label_->bounds().y() + | |
367 main_text_label_->height() + | |
368 kErrorLabelVerticalSpacing, | |
369 sz.width(), sz.height()); | |
370 } | |
371 | |
372 // Get the y-coordinate of our parent so we can position the text left of the | |
373 // buttons at the bottom. | |
374 gfx::Rect parent_bounds = parent()->GetContentsBounds(); | |
375 | |
376 sz = throbber_->GetPreferredSize(); | |
377 int throbber_topleft_x = views::kPanelHorizMargin; | |
378 int throbber_topleft_y = | |
379 parent_bounds.bottom() - sz.height() - views::kButtonVEdgeMargin - 3; | |
380 throbber_->SetBounds(throbber_topleft_x, throbber_topleft_y, | |
381 sz.width(), sz.height()); | |
382 | |
383 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. | |
384 sz = success_indicator_.GetPreferredSize(); | |
385 success_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, | |
386 sz.width(), sz.height()); | |
387 | |
388 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. | |
389 sz = update_available_indicator_.GetPreferredSize(); | |
390 update_available_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, | |
391 sz.width(), sz.height()); | |
392 | |
393 // This image is hidden (see ViewHierarchyChanged) and displayed on demand. | |
394 sz = timeout_indicator_.GetPreferredSize(); | |
395 timeout_indicator_.SetBounds(throbber_topleft_x, throbber_topleft_y, | |
396 sz.width(), sz.height()); | |
397 | |
398 // The update label should be at the bottom of the screen, to the right of the | |
399 // throbber. It vertically lines up with the ok button, so we make sure it | |
400 // doesn't extend into the ok button. | |
401 sz = update_label_.GetPreferredSize(); | |
402 int update_label_x = throbber_->x() + throbber_->width() + | |
403 views::kRelatedControlHorizontalSpacing; | |
404 views::DialogClientView* dialog_client_view = GetDialogClientView(); | |
405 int max_x = parent_bounds.width(); | |
406 if (dialog_client_view->ok_button() && | |
407 dialog_client_view->ok_button()->visible()) { | |
408 max_x = std::min(dialog_client_view->ok_button()->x(), max_x); | |
409 } | |
410 if (dialog_client_view->cancel_button() && | |
411 dialog_client_view->cancel_button()->visible()) { | |
412 max_x = std::min(dialog_client_view->cancel_button()->x(), max_x); | |
413 } | |
414 update_label_.SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
415 update_label_.SetBounds(update_label_x, | |
416 throbber_topleft_y + 1, | |
417 std::min(max_x - update_label_x, sz.width()), | |
418 sz.height()); | |
419 } | |
420 | |
421 void AboutChromeView::OnPaint(gfx::Canvas* canvas) { | |
422 views::View::OnPaint(canvas); | |
423 | |
424 // Draw the background image color (and the separator) across the dialog. | |
425 // This will become the background for the logo image at the top of the | |
426 // dialog. | |
427 gfx::ImageSkia* background = ui::ResourceBundle::GetSharedInstance(). | |
428 GetImageSkiaNamed(IDR_ABOUT_BACKGROUND_COLOR); | |
429 canvas->TileImageInt(*background, 0, 0, dialog_dimensions_.width(), | |
430 background->height()); | |
431 | |
432 gfx::Font font = ui::ResourceBundle::GetSharedInstance().GetFont( | |
433 ui::ResourceBundle::BaseFont); | |
434 | |
435 const gfx::Rect label_bounds = main_text_label_->bounds(); | |
436 | |
437 views::Link* link1 = | |
438 chromium_url_appears_first_ ? chromium_url_ : open_source_url_; | |
439 views::Link* link2 = | |
440 chromium_url_appears_first_ ? open_source_url_ : chromium_url_; | |
441 gfx::Rect* rect1 = chromium_url_appears_first_ ? | |
442 &chromium_url_rect_ : &open_source_url_rect_; | |
443 gfx::Rect* rect2 = chromium_url_appears_first_ ? | |
444 &open_source_url_rect_ : &chromium_url_rect_; | |
445 | |
446 // This struct keeps track of where to write the next word (which x,y | |
447 // pixel coordinate). This struct is updated after drawing text and checking | |
448 // if we need to wrap. | |
449 gfx::Size position; | |
450 // Draw the first text chunk and position the Chromium url. | |
451 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, | |
452 main_label_chunk1_, link1, rect1, &position, | |
453 text_direction_is_rtl_, label_bounds, font); | |
454 // Draw the second text chunk and position the Open Source url. | |
455 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, | |
456 main_label_chunk2_, link2, rect2, &position, | |
457 text_direction_is_rtl_, label_bounds, font); | |
458 // Draw the third text chunk (which has no URL associated with it). | |
459 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, | |
460 main_label_chunk3_, NULL, NULL, &position, | |
461 text_direction_is_rtl_, label_bounds, font); | |
462 | |
463 #if defined(GOOGLE_CHROME_BUILD) | |
464 // Insert a line break and some whitespace. | |
465 position.set_width(0); | |
466 position.Enlarge(0, font.GetHeight() + views::kRelatedControlVerticalSpacing); | |
467 | |
468 // And now the Terms of Service and position the TOS url. | |
469 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, | |
470 main_label_chunk4_, terms_of_service_url_, | |
471 &terms_of_service_url_rect_, &position, text_direction_is_rtl_, | |
472 label_bounds, font); | |
473 // The last text chunk doesn't have a URL associated with it. | |
474 view_text_utils::DrawTextAndPositionUrl(canvas, main_text_label_, | |
475 main_label_chunk5_, NULL, NULL, &position, | |
476 text_direction_is_rtl_, label_bounds, font); | |
477 | |
478 // Position the TOS URL within the main label. | |
479 terms_of_service_url_->SetBounds(terms_of_service_url_rect_.x(), | |
480 terms_of_service_url_rect_.y(), | |
481 terms_of_service_url_rect_.width(), | |
482 terms_of_service_url_rect_.height()); | |
483 #endif | |
484 | |
485 // Position the URLs within the main label. First position the Chromium URL | |
486 // within the main label. | |
487 chromium_url_->SetBounds(chromium_url_rect_.x(), | |
488 chromium_url_rect_.y(), | |
489 chromium_url_rect_.width(), | |
490 chromium_url_rect_.height()); | |
491 // Then position the Open Source URL within the main label. | |
492 open_source_url_->SetBounds(open_source_url_rect_.x(), | |
493 open_source_url_rect_.y(), | |
494 open_source_url_rect_.width(), | |
495 open_source_url_rect_.height()); | |
496 | |
497 // Save the height so we can set the bounds correctly. | |
498 main_text_label_height_ = position.height() + font.GetHeight(); | |
499 } | |
500 | |
501 void AboutChromeView::ViewHierarchyChanged(bool is_add, | |
502 views::View* parent, | |
503 views::View* child) { | |
504 // Since we want some of the controls to show up in the same visual row | |
505 // as the buttons, which are provided by the framework, we must add the | |
506 // buttons to the non-client view, which is the parent of this view. | |
507 // Similarly, when we're removed from the view hierarchy, we must take care | |
508 // to remove these items as well. | |
509 if (child == this) { | |
510 if (is_add) { | |
511 parent->AddChildView(&update_label_); | |
512 parent->AddChildView(throbber_.get()); | |
513 parent->AddChildView(&success_indicator_); | |
514 success_indicator_.SetVisible(false); | |
515 parent->AddChildView(&update_available_indicator_); | |
516 update_available_indicator_.SetVisible(false); | |
517 parent->AddChildView(&timeout_indicator_); | |
518 timeout_indicator_.SetVisible(false); | |
519 | |
520 #if defined(OS_WIN) && !defined(USE_AURA) | |
521 // On-demand updates for Chrome don't work in Vista RTM when UAC is turned | |
522 // off. So, in this case we just want the About box to not mention | |
523 // on-demand updates. Silent updates (in the background) should still | |
524 // work as before - enabling UAC or installing the latest service pack | |
525 // for Vista is another option. | |
526 if (!(base::win::GetVersion() == base::win::VERSION_VISTA && | |
527 (base::win::OSInfo::GetInstance()->service_pack().major == 0) && | |
528 !base::win::UserAccountControlIsEnabled())) { | |
529 UpdateStatus(UPGRADE_CHECK_STARTED, GOOGLE_UPDATE_NO_ERROR, string16()); | |
530 // CheckForUpdate(false, ...) means don't upgrade yet. | |
531 google_updater_->CheckForUpdate(false, GetWidget()->GetNativeWindow()); | |
532 } | |
533 #endif | |
534 } else { | |
535 parent->RemoveChildView(&update_label_); | |
536 parent->RemoveChildView(throbber_.get()); | |
537 parent->RemoveChildView(&success_indicator_); | |
538 parent->RemoveChildView(&update_available_indicator_); | |
539 parent->RemoveChildView(&timeout_indicator_); | |
540 } | |
541 } | |
542 } | |
543 | |
544 //////////////////////////////////////////////////////////////////////////////// | |
545 // AboutChromeView, views::DialogDelegate implementation: | |
546 | |
547 string16 AboutChromeView::GetDialogButtonLabel(ui::DialogButton button) const { | |
548 if (button == ui::DIALOG_BUTTON_OK) { | |
549 return l10n_util::GetStringUTF16(IDS_RELAUNCH_AND_UPDATE); | |
550 } else if (button == ui::DIALOG_BUTTON_CANCEL) { | |
551 if (restart_button_visible_) | |
552 return l10n_util::GetStringUTF16(IDS_NOT_NOW); | |
553 // The OK button (which is the default button) has been re-purposed to be | |
554 // 'Restart Now' so we want the Cancel button should have the label | |
555 // OK but act like a Cancel button in all other ways. | |
556 return l10n_util::GetStringUTF16(IDS_OK); | |
557 } | |
558 | |
559 NOTREACHED(); | |
560 return string16(); | |
561 } | |
562 | |
563 string16 AboutChromeView::GetWindowTitle() const { | |
564 return l10n_util::GetStringUTF16(IDS_ABOUT_CHROME_TITLE); | |
565 } | |
566 | |
567 bool AboutChromeView::IsDialogButtonEnabled(ui::DialogButton button) const { | |
568 if (button == ui::DIALOG_BUTTON_OK && !restart_button_visible_) | |
569 return false; | |
570 | |
571 return true; | |
572 } | |
573 | |
574 bool AboutChromeView::IsDialogButtonVisible(ui::DialogButton button) const { | |
575 if (button == ui::DIALOG_BUTTON_OK && !restart_button_visible_) | |
576 return false; | |
577 | |
578 return true; | |
579 } | |
580 | |
581 // (on ChromeOS) the default focus is ending up in the version field when | |
582 // the update button is hidden. This forces the focus to always be on the | |
583 // OK button (which is the dialog cancel button, see GetDialogButtonLabel | |
584 // above). | |
585 int AboutChromeView::GetDefaultDialogButton() const { | |
586 return ui::DIALOG_BUTTON_CANCEL; | |
587 } | |
588 | |
589 bool AboutChromeView::CanResize() const { | |
590 return false; | |
591 } | |
592 | |
593 bool AboutChromeView::CanMaximize() const { | |
594 return false; | |
595 } | |
596 | |
597 ui::ModalType AboutChromeView::GetModalType() const { | |
598 return ui::MODAL_TYPE_WINDOW; | |
599 } | |
600 | |
601 bool AboutChromeView::Accept() { | |
602 browser::AttemptRestart(); | |
603 return true; | |
604 } | |
605 | |
606 views::View* AboutChromeView::GetContentsView() { | |
607 return this; | |
608 } | |
609 | |
610 //////////////////////////////////////////////////////////////////////////////// | |
611 // AboutChromeView, views::LinkListener implementation: | |
612 void AboutChromeView::LinkClicked(views::Link* source, int event_flags) { | |
613 GURL url; | |
614 if (source == terms_of_service_url_) { | |
615 url = GURL(chrome::kChromeUITermsURL); | |
616 } else if (source == chromium_url_) { | |
617 url = GURL(chrome::kChromiumProjectURL); | |
618 } else if (source == open_source_url_) { | |
619 url = GURL(chrome::kChromeUICreditsURL); | |
620 } else { | |
621 NOTREACHED() << "Unknown link source"; | |
622 } | |
623 | |
624 OpenURLParams params( | |
625 url, Referrer(), NEW_WINDOW, content::PAGE_TRANSITION_LINK, false); | |
626 browser_->OpenURL(params); | |
627 } | |
628 | |
629 #if defined(OS_WIN) && !defined(USE_AURA) | |
630 //////////////////////////////////////////////////////////////////////////////// | |
631 // AboutChromeView, GoogleUpdateStatusListener implementation: | |
632 | |
633 void AboutChromeView::OnReportResults(GoogleUpdateUpgradeResult result, | |
634 GoogleUpdateErrorCode error_code, | |
635 const string16& error_message, | |
636 const string16& version) { | |
637 // Drop the last reference to the object so that it gets cleaned up here. | |
638 google_updater_ = NULL; | |
639 | |
640 // Make a note of which version Google Update is reporting is the latest | |
641 // version. | |
642 new_version_available_ = version; | |
643 UpdateStatus(result, error_code, error_message); | |
644 } | |
645 | |
646 //////////////////////////////////////////////////////////////////////////////// | |
647 // AboutChromeView, private: | |
648 | |
649 void AboutChromeView::UpdateStatus(GoogleUpdateUpgradeResult result, | |
650 GoogleUpdateErrorCode error_code, | |
651 const string16& error_message) { | |
652 #if !defined(GOOGLE_CHROME_BUILD) | |
653 // For Chromium builds it would show an error message. | |
654 // But it looks weird because in fact there is no error, | |
655 // just the update server is not available for non-official builds. | |
656 return; | |
657 #endif | |
658 bool show_success_indicator = false; | |
659 bool show_update_available_indicator = false; | |
660 bool show_timeout_indicator = false; | |
661 bool show_throbber = false; | |
662 bool show_update_label = true; // Always visible, except at start. | |
663 | |
664 switch (result) { | |
665 case UPGRADE_STARTED: | |
666 content::RecordAction(UserMetricsAction("Upgrade_Started")); | |
667 show_throbber = true; | |
668 update_label_.SetText(l10n_util::GetStringUTF16(IDS_UPGRADE_STARTED)); | |
669 break; | |
670 case UPGRADE_CHECK_STARTED: | |
671 content::RecordAction(UserMetricsAction("UpgradeCheck_Started")); | |
672 show_throbber = true; | |
673 update_label_.SetText( | |
674 l10n_util::GetStringUTF16(IDS_UPGRADE_CHECK_STARTED)); | |
675 break; | |
676 case UPGRADE_IS_AVAILABLE: | |
677 content::RecordAction( | |
678 UserMetricsAction("UpgradeCheck_UpgradeIsAvailable")); | |
679 DCHECK(!google_updater_); // Should have been nulled out already. | |
680 google_updater_ = new GoogleUpdate(); | |
681 google_updater_->set_status_listener(this); | |
682 UpdateStatus(UPGRADE_STARTED, GOOGLE_UPDATE_NO_ERROR, string16()); | |
683 // CheckForUpdate(true,...) means perform upgrade if new version found. | |
684 google_updater_->CheckForUpdate(true, GetWidget()->GetNativeWindow()); | |
685 // TODO(seanparent): Need to see if this code needs to change to | |
686 // force a machine restart. | |
687 return; | |
688 case UPGRADE_ALREADY_UP_TO_DATE: { | |
689 // The extra version check is necessary on Windows because the application | |
690 // may be already up to date on disk though the running app is still | |
691 // out of date. Chrome OS doesn't quite have this issue since the | |
692 // OS/App are updated together. If a newer version of the OS has been | |
693 // staged then UPGRADE_SUCESSFUL will be returned. | |
694 // Google Update reported that Chrome is up-to-date. Now make sure that we | |
695 // are running the latest version and if not, notify the user by falling | |
696 // into the next case of UPGRADE_SUCCESSFUL. | |
697 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); | |
698 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
699 chrome::VersionInfo version_info; | |
700 Version installed_version; | |
701 InstallUtil::GetChromeVersion(dist, false, &installed_version); | |
702 if (!installed_version.IsValid()) { | |
703 // User-level Chrome is not installed, check system-level. | |
704 InstallUtil::GetChromeVersion(dist, true, &installed_version); | |
705 } | |
706 Version running_version(version_info.Version()); | |
707 if (!installed_version.IsValid() || | |
708 (installed_version.CompareTo(running_version) <= 0)) { | |
709 content::RecordAction( | |
710 UserMetricsAction("UpgradeCheck_AlreadyUpToDate")); | |
711 string16 update_label_text = l10n_util::GetStringFUTF16( | |
712 IDS_UPGRADE_ALREADY_UP_TO_DATE, | |
713 ASCIIToUTF16(version_info.Version())); | |
714 if (base::i18n::IsRTL()) { | |
715 update_label_text.push_back( | |
716 static_cast<wchar_t>(base::i18n::kLeftToRightMark)); | |
717 } | |
718 update_label_.SetText(update_label_text); | |
719 show_success_indicator = true; | |
720 break; | |
721 } | |
722 // No break here as we want to notify user about upgrade if there is one. | |
723 } | |
724 case UPGRADE_SUCCESSFUL: { | |
725 if (result == UPGRADE_ALREADY_UP_TO_DATE) | |
726 content::RecordAction( | |
727 UserMetricsAction("UpgradeCheck_AlreadyUpgraded")); | |
728 else | |
729 content::RecordAction(UserMetricsAction("UpgradeCheck_Upgraded")); | |
730 restart_button_visible_ = true; | |
731 update_label_.SetText( | |
732 l10n_util::GetStringUTF16(IDS_OLD_UPGRADE_SUCCESSFUL_RELAUNCH)); | |
733 show_success_indicator = true; | |
734 break; | |
735 } | |
736 case UPGRADE_ERROR: { | |
737 content::RecordAction(UserMetricsAction("UpgradeCheck_Error")); | |
738 if (!error_message.empty() && error_label_) { | |
739 error_label_->SetText( | |
740 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK, | |
741 error_message)); | |
742 int added_height = EnlargeWindowSizeIfNeeded(); | |
743 dialog_dimensions_.set_height(dialog_dimensions_.height() + | |
744 added_height); | |
745 } | |
746 restart_button_visible_ = false; | |
747 if (error_code != GOOGLE_UPDATE_DISABLED_BY_POLICY) { | |
748 update_label_.SetText( | |
749 l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, error_code)); | |
750 } else { | |
751 update_label_.SetText( | |
752 l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY)); | |
753 } | |
754 show_timeout_indicator = true; | |
755 break; | |
756 } | |
757 default: | |
758 NOTREACHED(); | |
759 } | |
760 | |
761 success_indicator_.SetVisible(show_success_indicator); | |
762 update_available_indicator_.SetVisible(show_update_available_indicator); | |
763 timeout_indicator_.SetVisible(show_timeout_indicator); | |
764 update_label_.SetVisible(show_update_label); | |
765 throbber_->SetVisible(show_throbber); | |
766 if (show_throbber) | |
767 throbber_->Start(); | |
768 else | |
769 throbber_->Stop(); | |
770 | |
771 // We have updated controls on the parent, so we need to update its layout. | |
772 parent()->Layout(); | |
773 | |
774 // Check button may have appeared/disappeared. We cannot call this during | |
775 // ViewHierarchyChanged because the view hasn't been added to a Widget yet. | |
776 if (GetWidget()) | |
777 GetDialogClientView()->UpdateDialogButtons(); | |
778 } | |
779 | |
780 int AboutChromeView::EnlargeWindowSizeIfNeeded() { | |
781 if (!error_label_ || error_label_->text().empty()) | |
782 return 0; | |
783 | |
784 // This will enlarge the window each time the function is called, which is | |
785 // fine since we only receive status once from Google Update. | |
786 gfx::Rect window_rect = GetWidget()->GetWindowBoundsInScreen(); | |
787 int height = error_label_->GetHeightForWidth( | |
788 dialog_dimensions_.width() - (2 * views::kPanelHorizMargin)) + | |
789 views::kRelatedControlVerticalSpacing; | |
790 window_rect.set_height(window_rect.height() + height); | |
791 GetWidget()->SetBounds(window_rect); | |
792 | |
793 return height; | |
794 } | |
795 | |
796 #endif | |
OLD | NEW |