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