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
{
52 OriginChipExtensionIcon(LocationIconView
* icon_view
,
54 const extensions::Extension
* extension
);
55 virtual ~OriginChipExtensionIcon();
57 // IconImage::Observer:
58 virtual void OnExtensionIconImageChanged(
59 extensions::IconImage
* image
) OVERRIDE
;
62 LocationIconView
* icon_view_
;
63 scoped_ptr
<extensions::IconImage
> icon_image_
;
65 DISALLOW_COPY_AND_ASSIGN(OriginChipExtensionIcon
);
68 OriginChipExtensionIcon::OriginChipExtensionIcon(
69 LocationIconView
* icon_view
,
71 const extensions::Extension
* extension
)
72 : icon_view_(icon_view
),
73 icon_image_(new extensions::IconImage(
76 extensions::IconsInfo::GetIcons(extension
),
77 extension_misc::EXTENSION_ICON_BITTY
,
78 extensions::util::GetDefaultAppIcon(),
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
) {
93 icon_view_
->SetImage(&icon_image_
->image_skia());
97 // OriginChipView -------------------------------------------------------------
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
)
133 OriginChipView::OriginChipView(LocationBarView
* location_bar_view
,
135 const gfx::FontList
& font_list
)
136 : LabelButton(this, base::string16()),
137 location_bar_view_(location_bar_view
),
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
) {
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_
))
192 url_displayed_
= url
;
193 url_malware_
= url_malware
;
194 security_level_
= security_level
;
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
);
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(),
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(
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
));
241 extension_icon_
.reset();
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
)
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
,
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())
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();
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() +
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
,
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
);
354 views::LabelButton::OnPaintBorder(canvas
);
358 // TODO(gbillock): Make the LocationBarView or OmniboxView the listener for
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);
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
) {