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.
5 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
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"
24 using views::GridLayout
;
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.
30 const int kWidthOfDescriptionText
= 330;
32 // We subtract 2 to account for the natural button padding, and
33 // to bring the separation visually in line with the row separation
35 const int kButtonPadding
= views::kRelatedButtonHSpacing
- 2;
37 // The URL to be used to re-install Chrome when auto-update failed for too long.
38 const char kDownloadChromeUrl
[] = "https://www.google.com/chrome/?&brand=CHWL"
39 "&utm_campaign=en&utm_source=en-et-na-us-chrome-bubble&utm_medium=et";
41 // The maximum number of ignored bubble we track in the NumLaterPerReinstall
43 const int kMaxIgnored
= 50;
44 // The number of buckets we want the NumLaterPerReinstall histogram to use.
45 const int kNumIgnoredBuckets
= 5;
49 // OutdatedUpgradeBubbleView ---------------------------------------------------
51 OutdatedUpgradeBubbleView
* OutdatedUpgradeBubbleView::upgrade_bubble_
= NULL
;
52 int OutdatedUpgradeBubbleView::num_ignored_bubbles_
= 0;
55 void OutdatedUpgradeBubbleView::ShowBubble(views::View
* anchor_view
,
56 content::PageNavigator
* navigator
) {
59 upgrade_bubble_
= new OutdatedUpgradeBubbleView(anchor_view
, navigator
);
60 views::BubbleDelegateView::CreateBubble(upgrade_bubble_
);
61 upgrade_bubble_
->StartFade(true);
62 content::RecordAction(
63 base::UserMetricsAction("OutdatedUpgradeBubble.Show"));
66 bool 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))
76 OutdatedUpgradeBubbleView::~OutdatedUpgradeBubbleView() {
77 if (!chose_to_reinstall_
&& num_ignored_bubbles_
< kMaxIgnored
)
78 ++num_ignored_bubbles_
;
81 views::View
* OutdatedUpgradeBubbleView::GetInitiallyFocusedView() {
82 return reinstall_button_
;
85 void 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
;
93 void OutdatedUpgradeBubbleView::Init() {
94 base::string16
product_name(
95 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
));
96 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
97 reinstall_button_
= new views::LabelButton(
98 this, l10n_util::GetStringFUTF16(IDS_REINSTALL_APP
, product_name
));
99 reinstall_button_
->SetStyle(views::Button::STYLE_BUTTON
);
100 reinstall_button_
->SetIsDefault(true);
101 reinstall_button_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::BoldFont
));
103 later_button_
= new views::LabelButton(
104 this, l10n_util::GetStringUTF16(IDS_LATER
));
105 later_button_
->SetStyle(views::Button::STYLE_BUTTON
);
107 views::Label
* title_label
= new views::Label(
108 l10n_util::GetStringFUTF16(IDS_UPGRADE_BUBBLE_TITLE
, product_name
));
109 title_label
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
110 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
112 views::Label
* text_label
= new views::Label(
113 l10n_util::GetStringFUTF16(IDS_UPGRADE_BUBBLE_TEXT
, product_name
));
114 text_label
->SetMultiLine(true);
115 text_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
117 views::ImageView
* image_view
= new views::ImageView();
118 image_view
->SetImage(rb
.GetImageSkiaNamed(IDR_UPDATE_MENU_SEVERITY_HIGH
));
120 GridLayout
* layout
= new GridLayout(this);
121 SetLayoutManager(layout
);
123 const int kIconTitleColumnSetId
= 0;
124 views::ColumnSet
* cs
= layout
->AddColumnSet(kIconTitleColumnSetId
);
126 // Top (icon-title) row.
127 cs
->AddColumn(GridLayout::LEADING
, GridLayout::CENTER
, 0,
128 GridLayout::USE_PREF
, 0, 0);
129 cs
->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing
);
130 cs
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
, 0,
131 GridLayout::USE_PREF
, 0, 0);
132 cs
->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing
);
134 // Middle (text) row.
135 const int kTextColumnSetId
= 1;
136 cs
= layout
->AddColumnSet(kTextColumnSetId
);
137 cs
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 1,
138 GridLayout::FIXED
, kWidthOfDescriptionText
, 0);
140 // Bottom (buttons) row.
141 const int kButtonsColumnSetId
= 2;
142 cs
= layout
->AddColumnSet(kButtonsColumnSetId
);
143 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
144 cs
->AddColumn(GridLayout::LEADING
, GridLayout::TRAILING
, 0,
145 GridLayout::USE_PREF
, 0, 0);
146 cs
->AddPaddingColumn(0, kButtonPadding
);
147 cs
->AddColumn(GridLayout::LEADING
, GridLayout::TRAILING
, 0,
148 GridLayout::USE_PREF
, 0, 0);
150 layout
->StartRow(0, kIconTitleColumnSetId
);
151 layout
->AddView(image_view
);
152 layout
->AddView(title_label
);
154 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
155 layout
->StartRow(0, kTextColumnSetId
);
156 layout
->AddView(text_label
);
158 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
160 layout
->StartRow(0, kButtonsColumnSetId
);
161 layout
->AddView(reinstall_button_
);
162 layout
->AddView(later_button_
);
164 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
));
167 OutdatedUpgradeBubbleView::OutdatedUpgradeBubbleView(
168 views::View
* anchor_view
, content::PageNavigator
* navigator
)
169 : BubbleDelegateView(anchor_view
, views::BubbleBorder::TOP_RIGHT
),
170 chose_to_reinstall_(false),
171 reinstall_button_(NULL
),
173 navigator_(navigator
) {
174 // Compensate for built-in vertical padding in the anchor view's image.
175 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
178 void OutdatedUpgradeBubbleView::ButtonPressed(
179 views::Button
* sender
, const ui::Event
& event
) {
180 if (event
.IsMouseEvent() &&
181 !(static_cast<const ui::MouseEvent
*>(&event
))->IsOnlyLeftMouseButton()) {
184 HandleButtonPressed(sender
);
187 void OutdatedUpgradeBubbleView::HandleButtonPressed(views::Button
* sender
) {
188 if (sender
== reinstall_button_
) {
189 DCHECK(UpgradeDetector::GetInstance()->is_outdated_install());
190 chose_to_reinstall_
= true;
191 UMA_HISTOGRAM_CUSTOM_COUNTS(
192 "OutdatedUpgradeBubble.NumLaterPerReinstall", num_ignored_bubbles_
,
193 0, kMaxIgnored
, kNumIgnoredBuckets
);
194 content::RecordAction(
195 base::UserMetricsAction("OutdatedUpgradeBubble.Reinstall"));
196 navigator_
->OpenURL(content::OpenURLParams(GURL(kDownloadChromeUrl
),
199 content::PAGE_TRANSITION_LINK
,
202 DCHECK_EQ(later_button_
, sender
);
203 content::RecordAction(
204 base::UserMetricsAction("OutdatedUpgradeBubble.Later"));