[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / location_bar / origin_chip_view.cc
blobb73eca9167105216c73af4b999084519f9d22ea4
1 // Copyright 2014 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/location_bar/origin_chip_view.h"
7 #include "base/files/file_path.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/extensions/extension_icon_image.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/favicon/favicon_tab_helper.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
18 #include "chrome/browser/safe_browsing/ui_manager.h"
19 #include "chrome/browser/search/search.h"
20 #include "chrome/browser/themes/theme_properties.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/elide_url.h"
23 #include "chrome/browser/ui/omnibox/omnibox_view.h"
24 #include "chrome/browser/ui/toolbar/origin_chip_info.h"
25 #include "chrome/browser/ui/toolbar/toolbar_model.h"
26 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/url_constants.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/common/constants.h"
33 #include "extensions/common/manifest_handlers/icons_handler.h"
34 #include "grit/generated_resources.h"
35 #include "grit/theme_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/gfx/animation/slide_animation.h"
40 #include "ui/gfx/canvas.h"
41 #include "ui/gfx/font_list.h"
42 #include "ui/views/background.h"
43 #include "ui/views/controls/button/label_button.h"
44 #include "ui/views/controls/button/label_button_border.h"
45 #include "ui/views/painter.h"
48 // OriginChipExtensionIcon ----------------------------------------------------
50 class OriginChipExtensionIcon : public extensions::IconImage::Observer {
51 public:
52 OriginChipExtensionIcon(LocationIconView* icon_view,
53 Profile* profile,
54 const extensions::Extension* extension);
55 virtual ~OriginChipExtensionIcon();
57 // IconImage::Observer:
58 virtual void OnExtensionIconImageChanged(
59 extensions::IconImage* image) OVERRIDE;
61 private:
62 LocationIconView* icon_view_;
63 scoped_ptr<extensions::IconImage> icon_image_;
65 DISALLOW_COPY_AND_ASSIGN(OriginChipExtensionIcon);
68 OriginChipExtensionIcon::OriginChipExtensionIcon(
69 LocationIconView* icon_view,
70 Profile* profile,
71 const extensions::Extension* extension)
72 : icon_view_(icon_view),
73 icon_image_(new extensions::IconImage(
74 profile,
75 extension,
76 extensions::IconsInfo::GetIcons(extension),
77 extension_misc::EXTENSION_ICON_BITTY,
78 extensions::util::GetDefaultAppIcon(),
79 this)) {
80 // Forces load of the image.
81 icon_image_->image_skia().GetRepresentation(1.0f);
83 if (!icon_image_->image_skia().image_reps().empty())
84 OnExtensionIconImageChanged(icon_image_.get());
87 OriginChipExtensionIcon::~OriginChipExtensionIcon() {
90 void OriginChipExtensionIcon::OnExtensionIconImageChanged(
91 extensions::IconImage* image) {
92 if (icon_view_)
93 icon_view_->SetImage(&icon_image_->image_skia());
97 // OriginChipView -------------------------------------------------------------
99 namespace {
101 const int kEdgeThickness = 5;
102 const int k16x16IconLeadingSpacing = 1;
103 const int k16x16IconTrailingSpacing = 2;
104 const int kIconTextSpacing = 3;
105 const int kTrailingLabelMargin = 0;
107 const int kNormalImages[3][9] = {
108 IMAGE_GRID(IDR_ORIGIN_CHIP_NORMAL),
109 IMAGE_GRID(IDR_ORIGIN_CHIP_HOVER),
110 IMAGE_GRID(IDR_ORIGIN_CHIP_PRESSED)
113 const int kMalwareImages[3][9] = {
114 IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_NORMAL),
115 IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_HOVER),
116 IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_PRESSED)
119 const int kBrokenSSLImages[3][9] = {
120 IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_NORMAL),
121 IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_HOVER),
122 IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_PRESSED)
125 const int kEVImages[3][9] = {
126 IMAGE_GRID(IDR_ORIGIN_CHIP_EV_NORMAL),
127 IMAGE_GRID(IDR_ORIGIN_CHIP_EV_HOVER),
128 IMAGE_GRID(IDR_ORIGIN_CHIP_EV_PRESSED)
131 } // namespace
133 OriginChipView::OriginChipView(LocationBarView* location_bar_view,
134 Profile* profile,
135 const gfx::FontList& font_list)
136 : LabelButton(this, base::string16()),
137 location_bar_view_(location_bar_view),
138 profile_(profile),
139 showing_16x16_icon_(false) {
140 scoped_refptr<SafeBrowsingService> sb_service =
141 g_browser_process->safe_browsing_service();
142 // May not be set for unit tests.
143 if (sb_service && sb_service->ui_manager())
144 sb_service->ui_manager()->AddObserver(this);
146 SetFontList(font_list);
148 image()->EnableCanvasFlippingForRTLUI(false);
150 // TODO(gbillock): Would be nice to just use stock LabelButton stuff here.
151 location_icon_view_ = new LocationIconView(location_bar_view_);
152 // Make location icon hover events count as hovering the origin chip.
153 location_icon_view_->set_interactive(false);
154 location_icon_view_->ShowTooltip(true);
155 AddChildView(location_icon_view_);
157 host_label_ = new views::Label(base::string16(), GetFontList());
158 AddChildView(host_label_);
160 fade_in_animation_.reset(new gfx::SlideAnimation(this));
161 fade_in_animation_->SetTweenType(gfx::Tween::LINEAR);
162 fade_in_animation_->SetSlideDuration(300);
165 OriginChipView::~OriginChipView() {
166 scoped_refptr<SafeBrowsingService> sb_service =
167 g_browser_process->safe_browsing_service();
168 if (sb_service.get() && sb_service->ui_manager())
169 sb_service->ui_manager()->RemoveObserver(this);
172 void OriginChipView::Update(content::WebContents* web_contents) {
173 if (!web_contents)
174 return;
176 // Note: security level can change async as the connection is made.
177 GURL url = location_bar_view_->GetToolbarModel()->GetURL();
178 const ToolbarModel::SecurityLevel security_level =
179 location_bar_view_->GetToolbarModel()->GetSecurityLevel(true);
181 bool url_malware = OriginChip::IsMalware(url, web_contents);
183 // TODO(gbillock): We persist a malware setting while a new WebContents
184 // content is loaded, meaning that we end up transiently marking a safe
185 // page as malware. Need to fix that.
187 if ((url == url_displayed_) &&
188 (security_level == security_level_) &&
189 (url_malware == url_malware_))
190 return;
192 url_displayed_ = url;
193 url_malware_ = url_malware;
194 security_level_ = security_level;
196 if (url_malware_) {
197 SetBorderImages(kMalwareImages);
198 } else if (security_level_ == ToolbarModel::SECURITY_ERROR) {
199 SetBorderImages(kBrokenSSLImages);
200 } else if (security_level_ == ToolbarModel::EV_SECURE) {
201 SetBorderImages(kEVImages);
202 } else {
203 SetBorderImages(kNormalImages);
206 base::string16 host =
207 OriginChip::LabelFromURLForProfile(url_displayed_, profile_);
208 if (security_level_ == ToolbarModel::EV_SECURE) {
209 host = l10n_util::GetStringFUTF16(IDS_SITE_CHIP_EV_SSL_LABEL,
210 location_bar_view_->GetToolbarModel()->GetEVCertName(),
211 host);
213 host_label_->SetText(host);
214 host_label_->SetTooltipText(base::UTF8ToUTF16(url.spec()));
215 host_label_->SetElideBehavior(views::Label::NO_ELIDE);
217 int icon = location_bar_view_->GetToolbarModel()->GetIconForSecurityLevel(
218 security_level_);
219 showing_16x16_icon_ = false;
221 if (url_displayed_.is_empty() ||
222 url_displayed_.SchemeIs(content::kChromeUIScheme)) {
223 icon = IDR_PRODUCT_LOGO_16;
224 showing_16x16_icon_ = true;
227 location_icon_view_->SetImage(GetThemeProvider()->GetImageSkiaNamed(icon));
229 if (url_displayed_.SchemeIs(extensions::kExtensionScheme)) {
230 icon = IDR_EXTENSIONS_FAVICON;
231 showing_16x16_icon_ = true;
232 location_icon_view_->SetImage(GetThemeProvider()->GetImageSkiaNamed(icon));
234 ExtensionService* service =
235 extensions::ExtensionSystem::Get(profile_)->extension_service();
236 const extensions::Extension* extension =
237 service->extensions()->GetExtensionOrAppByURL(url_displayed_);
238 extension_icon_.reset(
239 new OriginChipExtensionIcon(location_icon_view_, profile_, extension));
240 } else {
241 extension_icon_.reset();
244 Layout();
245 SchedulePaint();
248 void OriginChipView::OnChanged() {
249 Update(location_bar_view_->GetWebContents());
250 // TODO(gbillock): Also need to potentially repaint infobars to make sure the
251 // arrows are pointing to the right spot. Only needed for some edge cases.
254 int OriginChipView::ElideDomainTarget(int target_max_width) {
255 base::string16 host =
256 OriginChip::LabelFromURLForProfile(url_displayed_, profile_);
257 host_label_->SetText(host);
258 int width = GetPreferredSize().width();
259 if (width <= target_max_width)
260 return width;
262 gfx::Size label_size = host_label_->GetPreferredSize();
263 int padding_width = width - label_size.width();
265 host_label_->SetText(ElideHost(
266 location_bar_view_->GetToolbarModel()->GetURL(),
267 host_label_->font_list(), target_max_width - padding_width));
268 return GetPreferredSize().width();
271 void OriginChipView::FadeIn() {
272 fade_in_animation_->Show();
275 gfx::Size OriginChipView::GetPreferredSize() const {
276 gfx::Size label_size = host_label_->GetPreferredSize();
277 gfx::Size icon_size = location_icon_view_->GetPreferredSize();
278 int icon_spacing = showing_16x16_icon_ ?
279 (k16x16IconLeadingSpacing + k16x16IconTrailingSpacing) : 0;
280 return gfx::Size(kEdgeThickness + icon_size.width() + icon_spacing +
281 kIconTextSpacing + label_size.width() +
282 kTrailingLabelMargin + kEdgeThickness,
283 icon_size.height());
286 void OriginChipView::SetBorderImages(const int images[3][9]) {
287 scoped_ptr<views::LabelButtonBorder> border(
288 new views::LabelButtonBorder(views::Button::STYLE_BUTTON));
290 views::Painter* painter = views::Painter::CreateImageGridPainter(images[0]);
291 border->SetPainter(false, Button::STATE_NORMAL, painter);
292 painter = views::Painter::CreateImageGridPainter(images[1]);
293 border->SetPainter(false, Button::STATE_HOVERED, painter);
294 painter = views::Painter::CreateImageGridPainter(images[2]);
295 border->SetPainter(false, Button::STATE_PRESSED, painter);
297 SetBorder(border.PassAs<views::Border>());
299 // Calculate a representative background color of the provided image grid and
300 // set it as the background color of the host label in order to color the text
301 // appropriately. We grab the color of the middle pixel of the middle image
302 // of the background, which we treat as the representative color of the entire
303 // background (reasonable, given the current appearance of these images).
304 const SkBitmap& bitmap(
305 GetThemeProvider()->GetImageSkiaNamed(
306 images[0][4])->GetRepresentation(1.0f).sk_bitmap());
307 SkAutoLockPixels pixel_lock(bitmap);
308 host_label_->SetBackgroundColor(
309 bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2));
312 void OriginChipView::AnimationProgressed(const gfx::Animation* animation) {
313 if (animation == fade_in_animation_.get())
314 SchedulePaint();
315 else
316 views::LabelButton::AnimationProgressed(animation);
319 void OriginChipView::AnimationEnded(const gfx::Animation* animation) {
320 if (animation == fade_in_animation_.get())
321 fade_in_animation_->Reset();
322 else
323 views::LabelButton::AnimationEnded(animation);
326 void OriginChipView::Layout() {
327 // TODO(gbillock): Eventually we almost certainly want to use
328 // LocationBarLayout for leading and trailing decorations.
330 location_icon_view_->SetBounds(
331 kEdgeThickness + (showing_16x16_icon_ ? k16x16IconLeadingSpacing : 0),
332 LocationBarView::kNormalEdgeThickness,
333 location_icon_view_->GetPreferredSize().width(),
334 height() - 2 * LocationBarView::kNormalEdgeThickness);
336 int host_label_x = location_icon_view_->x() + location_icon_view_->width() +
337 kIconTextSpacing;
338 host_label_x += showing_16x16_icon_ ? k16x16IconTrailingSpacing : 0;
339 int host_label_width =
340 width() - host_label_x - kEdgeThickness - kTrailingLabelMargin;
341 host_label_->SetBounds(host_label_x,
342 LocationBarView::kNormalEdgeThickness,
343 host_label_width,
344 height() - 2 * LocationBarView::kNormalEdgeThickness);
347 void OriginChipView::OnPaintBorder(gfx::Canvas* canvas) {
348 if (fade_in_animation_->is_animating()) {
349 canvas->SaveLayerAlpha(static_cast<uint8>(
350 fade_in_animation_->CurrentValueBetween(0, 255)));
351 views::LabelButton::OnPaintBorder(canvas);
352 canvas->Restore();
353 } else {
354 views::LabelButton::OnPaintBorder(canvas);
358 // TODO(gbillock): Make the LocationBarView or OmniboxView the listener for
359 // this button.
360 void OriginChipView::ButtonPressed(views::Button* sender,
361 const ui::Event& event) {
362 // See if the event needs to be passed to the LocationIconView.
363 if (event.IsMouseEvent() || (event.type() == ui::ET_GESTURE_TAP)) {
364 location_icon_view_->set_interactive(true);
365 const ui::LocatedEvent& located_event =
366 static_cast<const ui::LocatedEvent&>(event);
367 if (GetEventHandlerForPoint(located_event.location()) ==
368 location_icon_view_) {
369 location_icon_view_->page_info_helper()->ProcessEvent(located_event);
370 location_icon_view_->set_interactive(false);
371 return;
373 location_icon_view_->set_interactive(false);
376 UMA_HISTOGRAM_COUNTS("OriginChip.Pressed", 1);
377 content::RecordAction(base::UserMetricsAction("OriginChipPress"));
379 location_bar_view_->ShowURL();
382 // Note: When OnSafeBrowsingHit would be called, OnSafeBrowsingMatch will
383 // have already been called.
384 void OriginChipView::OnSafeBrowsingHit(
385 const SafeBrowsingUIManager::UnsafeResource& resource) {}
387 void OriginChipView::OnSafeBrowsingMatch(
388 const SafeBrowsingUIManager::UnsafeResource& resource) {
389 OnChanged();