outdated_upgrade_bubble_view.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2013 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/outdated_upgrade_bubble_view.h" 6 7#include "base/metrics/histogram.h" 8#include "base/path_service.h" 9#include "base/prefs/pref_service.h" 10#include "chrome/browser/browser_process.h" 11#include "chrome/browser/ui/views/elevation_icon_setter.h" 12#include "chrome/browser/upgrade_detector.h" 13#include "chrome/common/pref_names.h" 14#include "chrome/grit/chromium_strings.h" 15#include "chrome/grit/generated_resources.h" 16#include "content/public/browser/browser_thread.h" 17#include "content/public/browser/page_navigator.h" 18#include "content/public/browser/user_metrics.h" 19#include "grit/theme_resources.h" 20#include "ui/base/l10n/l10n_util.h" 21#include "ui/base/resource/resource_bundle.h" 22#include "ui/views/controls/button/label_button.h" 23#include "ui/views/controls/image_view.h" 24#include "ui/views/controls/label.h" 25#include "ui/views/layout/grid_layout.h" 26#include "ui/views/layout/layout_constants.h" 27#include "ui/views/widget/widget.h" 28#include "url/gurl.h" 29 30#if defined(OS_WIN) 31#include "chrome/installer/util/google_update_util.h" 32#endif 33 34namespace { 35 36// Fixed width of the column holding the description label of the bubble. 37// TODO(mad): Make sure there is enough room for all languages. 38const int kWidthOfDescriptionText = 330; 39 40// We subtract 2 to account for the natural button padding, and 41// to bring the separation visually in line with the row separation 42// height. 43const int kButtonPadding = views::kRelatedButtonHSpacing - 2; 44 45// The URL to be used to re-install Chrome when auto-update failed for too long. 46const char kDownloadChromeUrl[] = "https://www.google.com/chrome/?&brand=CHWL" 47 "&utm_campaign=en&utm_source=en-et-na-us-chrome-bubble&utm_medium=et"; 48 49// The maximum number of ignored bubble we track in the NumLaterPerReinstall 50// histogram. 51const int kMaxIgnored = 50; 52// The number of buckets we want the NumLaterPerReinstall histogram to use. 53const int kNumIgnoredBuckets = 5; 54 55} // namespace 56 57// OutdatedUpgradeBubbleView --------------------------------------------------- 58 59OutdatedUpgradeBubbleView* OutdatedUpgradeBubbleView::upgrade_bubble_ = NULL; 60int OutdatedUpgradeBubbleView::num_ignored_bubbles_ = 0; 61 62// static 63void OutdatedUpgradeBubbleView::ShowBubble(views::View* anchor_view, 64 content::PageNavigator* navigator, 65 bool auto_update_enabled) { 66 if (IsShowing()) 67 return; 68 upgrade_bubble_ = new OutdatedUpgradeBubbleView( 69 anchor_view, navigator, auto_update_enabled); 70 views::BubbleDelegateView::CreateBubble(upgrade_bubble_)->Show(); 71 content::RecordAction(base::UserMetricsAction( 72 auto_update_enabled ? "OutdatedUpgradeBubble.Show" 73 : "OutdatedUpgradeBubble.ShowNoAU")); 74} 75 76bool OutdatedUpgradeBubbleView::IsAvailable() { 77// This should only work on non-Chrome OS desktop platforms. 78#if defined(OS_WIN) || defined(OS_MACOSX) || \ 79 (defined(OS_LINUX) && !defined(OS_CHROMEOS)) 80 return true; 81#else 82 return false; 83#endif 84} 85 86OutdatedUpgradeBubbleView::~OutdatedUpgradeBubbleView() { 87 if (!accepted_ && num_ignored_bubbles_ < kMaxIgnored) 88 ++num_ignored_bubbles_; 89 90 // Ensure |elevation_icon_setter_| is destroyed before |accept_button_|. 91 elevation_icon_setter_.reset(); 92} 93 94views::View* OutdatedUpgradeBubbleView::GetInitiallyFocusedView() { 95 return accept_button_; 96} 97 98void OutdatedUpgradeBubbleView::WindowClosing() { 99 // Reset |upgrade_bubble_| here, not in destructor, because destruction is 100 // asynchronous and ShowBubble may be called before full destruction and 101 // would attempt to show a bubble that is closing. 102 DCHECK_EQ(upgrade_bubble_, this); 103 upgrade_bubble_ = NULL; 104} 105 106void OutdatedUpgradeBubbleView::Init() { 107 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 108 accept_button_ = new views::LabelButton( 109 this, l10n_util::GetStringUTF16( 110 auto_update_enabled_ ? IDS_REINSTALL_APP : IDS_REENABLE_UPDATES)); 111 accept_button_->SetStyle(views::Button::STYLE_BUTTON); 112 accept_button_->SetIsDefault(true); 113 accept_button_->SetFontList(rb.GetFontList(ui::ResourceBundle::BoldFont)); 114 elevation_icon_setter_.reset(new ElevationIconSetter(accept_button_)); 115 116 later_button_ = new views::LabelButton( 117 this, l10n_util::GetStringUTF16(IDS_LATER)); 118 later_button_->SetStyle(views::Button::STYLE_BUTTON); 119 120 views::Label* title_label = new views::Label( 121 l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_TITLE)); 122 title_label->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont)); 123 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 124 125 views::Label* text_label = new views::Label(l10n_util::GetStringUTF16( 126 auto_update_enabled_ ? IDS_UPGRADE_BUBBLE_TEXT 127 : IDS_UPGRADE_BUBBLE_REENABLE_TEXT)); 128 text_label->SetMultiLine(true); 129 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 130 131 views::ImageView* image_view = new views::ImageView(); 132 image_view->SetImage(rb.GetImageSkiaNamed(IDR_UPDATE_MENU_SEVERITY_HIGH)); 133 134 views::GridLayout* layout = new views::GridLayout(this); 135 SetLayoutManager(layout); 136 137 const int kIconTitleColumnSetId = 0; 138 views::ColumnSet* cs = layout->AddColumnSet(kIconTitleColumnSetId); 139 140 // Top (icon-title) row. 141 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0, 142 views::GridLayout::USE_PREF, 0, 0); 143 cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); 144 cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 0, 145 views::GridLayout::USE_PREF, 0, 0); 146 cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing); 147 148 // Middle (text) row. 149 const int kTextColumnSetId = 1; 150 cs = layout->AddColumnSet(kTextColumnSetId); 151 cs->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 152 views::GridLayout::FIXED, kWidthOfDescriptionText, 0); 153 154 // Bottom (buttons) row. 155 const int kButtonsColumnSetId = 2; 156 cs = layout->AddColumnSet(kButtonsColumnSetId); 157 cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing); 158 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::TRAILING, 0, 159 views::GridLayout::USE_PREF, 0, 0); 160 cs->AddPaddingColumn(0, kButtonPadding); 161 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::TRAILING, 0, 162 views::GridLayout::USE_PREF, 0, 0); 163 164 layout->StartRow(0, kIconTitleColumnSetId); 165 layout->AddView(image_view); 166 layout->AddView(title_label); 167 168 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); 169 layout->StartRow(0, kTextColumnSetId); 170 layout->AddView(text_label); 171 172 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); 173 174 layout->StartRow(0, kButtonsColumnSetId); 175 layout->AddView(accept_button_); 176 layout->AddView(later_button_); 177 178 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); 179} 180 181OutdatedUpgradeBubbleView::OutdatedUpgradeBubbleView( 182 views::View* anchor_view, 183 content::PageNavigator* navigator, 184 bool auto_update_enabled) 185 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), 186 auto_update_enabled_(auto_update_enabled), 187 accepted_(false), 188 accept_button_(NULL), 189 later_button_(NULL), 190 navigator_(navigator) { 191 // Compensate for built-in vertical padding in the anchor view's image. 192 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); 193} 194 195void OutdatedUpgradeBubbleView::ButtonPressed( 196 views::Button* sender, const ui::Event& event) { 197 if (event.IsMouseEvent() && 198 !(static_cast<const ui::MouseEvent*>(&event))->IsOnlyLeftMouseButton()) { 199 return; 200 } 201 HandleButtonPressed(sender); 202} 203 204void OutdatedUpgradeBubbleView::HandleButtonPressed(views::Button* sender) { 205 if (sender == accept_button_) { 206 accepted_ = true; 207 if (auto_update_enabled_) { 208 DCHECK(UpgradeDetector::GetInstance()->is_outdated_install()); 209 UMA_HISTOGRAM_CUSTOM_COUNTS( 210 "OutdatedUpgradeBubble.NumLaterPerReinstall", num_ignored_bubbles_, 211 0, kMaxIgnored, kNumIgnoredBuckets); 212 content::RecordAction( 213 base::UserMetricsAction("OutdatedUpgradeBubble.Reinstall")); 214 navigator_->OpenURL(content::OpenURLParams(GURL(kDownloadChromeUrl), 215 content::Referrer(), 216 NEW_FOREGROUND_TAB, 217 ui::PAGE_TRANSITION_LINK, 218 false)); 219#if defined(OS_WIN) 220 } else { 221 DCHECK(UpgradeDetector::GetInstance()->is_outdated_install_no_au()); 222 UMA_HISTOGRAM_CUSTOM_COUNTS( 223 "OutdatedUpgradeBubble.NumLaterPerEnableAU", num_ignored_bubbles_, 224 0, kMaxIgnored, kNumIgnoredBuckets); 225 content::RecordAction( 226 base::UserMetricsAction("OutdatedUpgradeBubble.EnableAU")); 227 // Record that the autoupdate flavour of the dialog has been shown. 228 if (g_browser_process->local_state()) { 229 g_browser_process->local_state()->SetBoolean( 230 prefs::kAttemptedToEnableAutoupdate, true); 231 } 232 233 // Re-enable updates by shelling out to setup.exe in the blocking pool. 234 content::BrowserThread::PostBlockingPoolTask( 235 FROM_HERE, 236 base::Bind(&google_update::ElevateIfNeededToReenableUpdates)); 237#endif // defined(OS_WIN) 238 } 239 } else { 240 DCHECK_EQ(later_button_, sender); 241 content::RecordAction( 242 base::UserMetricsAction("OutdatedUpgradeBubble.Later")); 243 } 244 GetWidget()->Close(); 245} 246