Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / location_bar / origin_chip_view.cc
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.
4
5 #include "chrome/browser/ui/views/location_bar/origin_chip_view.h"
6
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"
46
47
48 // OriginChipExtensionIcon ----------------------------------------------------
49
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();
56
57   // IconImage::Observer:
58   virtual void OnExtensionIconImageChanged(
59       extensions::IconImage* image) OVERRIDE;
60
61  private:
62   LocationIconView* icon_view_;
63   scoped_ptr<extensions::IconImage> icon_image_;
64
65   DISALLOW_COPY_AND_ASSIGN(OriginChipExtensionIcon);
66 };
67
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);
82
83   if (!icon_image_->image_skia().image_reps().empty())
84     OnExtensionIconImageChanged(icon_image_.get());
85 }
86
87 OriginChipExtensionIcon::~OriginChipExtensionIcon() {
88 }
89
90 void OriginChipExtensionIcon::OnExtensionIconImageChanged(
91     extensions::IconImage* image) {
92   if (icon_view_)
93     icon_view_->SetImage(&icon_image_->image_skia());
94 }
95
96
97 // OriginChipView -------------------------------------------------------------
98
99 namespace {
100
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;
106
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)
111 };
112
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)
117 };
118
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)
123 };
124
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)
129 };
130
131 }  // namespace
132
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);
145
146   SetFontList(font_list);
147
148   image()->EnableCanvasFlippingForRTLUI(false);
149
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_);
156
157   host_label_ = new views::Label(base::string16(), GetFontList());
158   AddChildView(host_label_);
159
160   fade_in_animation_.reset(new gfx::SlideAnimation(this));
161   fade_in_animation_->SetTweenType(gfx::Tween::LINEAR);
162   fade_in_animation_->SetSlideDuration(300);
163 }
164
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);
170 }
171
172 bool OriginChipView::ShouldShow() {
173   return chrome::ShouldDisplayOriginChipV2() &&
174       location_bar_view_->GetToolbarModel()->WouldOmitURLDueToOriginChip() &&
175       location_bar_view_->GetToolbarModel()->origin_chip_enabled();
176 }
177
178 void OriginChipView::Update(content::WebContents* web_contents) {
179   if (!web_contents)
180     return;
181
182   // Note: security level can change async as the connection is made.
183   GURL url = location_bar_view_->GetToolbarModel()->GetURL();
184   const ToolbarModel::SecurityLevel security_level =
185       location_bar_view_->GetToolbarModel()->GetSecurityLevel(true);
186
187   bool url_malware = OriginChip::IsMalware(url, web_contents);
188
189   // TODO(gbillock): We persist a malware setting while a new WebContents
190   // content is loaded, meaning that we end up transiently marking a safe
191   // page as malware. Need to fix that.
192
193   if ((url == url_displayed_) &&
194       (security_level == security_level_) &&
195       (url_malware == url_malware_))
196     return;
197
198   url_displayed_ = url;
199   url_malware_ = url_malware;
200   security_level_ = security_level;
201
202   if (url_malware_) {
203     SetBorderImages(kMalwareImages);
204   } else if (security_level_ == ToolbarModel::SECURITY_ERROR) {
205     SetBorderImages(kBrokenSSLImages);
206   } else if (security_level_ == ToolbarModel::EV_SECURE) {
207     SetBorderImages(kEVImages);
208   } else {
209     SetBorderImages(kNormalImages);
210   }
211
212   base::string16 host =
213       OriginChip::LabelFromURLForProfile(url_displayed_, profile_);
214   if (security_level_ == ToolbarModel::EV_SECURE) {
215     host = l10n_util::GetStringFUTF16(IDS_SITE_CHIP_EV_SSL_LABEL,
216         location_bar_view_->GetToolbarModel()->GetEVCertName(),
217         host);
218   }
219   host_label_->SetText(host);
220   host_label_->SetTooltipText(base::UTF8ToUTF16(url.spec()));
221   host_label_->SetElideBehavior(views::Label::NO_ELIDE);
222
223   int icon = location_bar_view_->GetToolbarModel()->GetIconForSecurityLevel(
224       security_level_);
225   showing_16x16_icon_ = false;
226
227   if (url_displayed_.is_empty() ||
228       url_displayed_.SchemeIs(content::kChromeUIScheme)) {
229     icon = IDR_PRODUCT_LOGO_16;
230     showing_16x16_icon_ = true;
231   }
232
233   location_icon_view_->SetImage(GetThemeProvider()->GetImageSkiaNamed(icon));
234
235   if (url_displayed_.SchemeIs(extensions::kExtensionScheme)) {
236     icon = IDR_EXTENSIONS_FAVICON;
237     showing_16x16_icon_ = true;
238     location_icon_view_->SetImage(GetThemeProvider()->GetImageSkiaNamed(icon));
239
240     ExtensionService* service =
241         extensions::ExtensionSystem::Get(profile_)->extension_service();
242     const extensions::Extension* extension =
243         service->extensions()->GetExtensionOrAppByURL(url_displayed_);
244     extension_icon_.reset(
245         new OriginChipExtensionIcon(location_icon_view_, profile_, extension));
246   } else {
247     extension_icon_.reset();
248   }
249
250   Layout();
251   SchedulePaint();
252 }
253
254 void OriginChipView::OnChanged() {
255   Update(location_bar_view_->GetWebContents());
256   // TODO(gbillock): Also need to potentially repaint infobars to make sure the
257   // arrows are pointing to the right spot. Only needed for some edge cases.
258 }
259
260 int OriginChipView::ElideDomainTarget(int target_max_width) {
261   base::string16 host =
262       OriginChip::LabelFromURLForProfile(url_displayed_, profile_);
263   host_label_->SetText(host);
264   int width = GetPreferredSize().width();
265   if (width <= target_max_width)
266     return width;
267
268   gfx::Size label_size = host_label_->GetPreferredSize();
269   int padding_width = width - label_size.width();
270
271   host_label_->SetText(ElideHost(
272       location_bar_view_->GetToolbarModel()->GetURL(),
273       host_label_->font_list(), target_max_width - padding_width));
274   return GetPreferredSize().width();
275 }
276
277 void OriginChipView::FadeIn() {
278   fade_in_animation_->Show();
279 }
280
281 gfx::Size OriginChipView::GetPreferredSize() {
282   gfx::Size label_size = host_label_->GetPreferredSize();
283   gfx::Size icon_size = location_icon_view_->GetPreferredSize();
284   int icon_spacing = showing_16x16_icon_ ?
285       (k16x16IconLeadingSpacing + k16x16IconTrailingSpacing) : 0;
286   return gfx::Size(kEdgeThickness + icon_size.width() + icon_spacing +
287                    kIconTextSpacing + label_size.width() +
288                    kTrailingLabelMargin + kEdgeThickness,
289                    icon_size.height());
290 }
291
292 void OriginChipView::SetBorderImages(const int images[3][9]) {
293   scoped_ptr<views::LabelButtonBorder> border(
294       new views::LabelButtonBorder(views::Button::STYLE_BUTTON));
295
296   views::Painter* painter = views::Painter::CreateImageGridPainter(images[0]);
297   border->SetPainter(false, Button::STATE_NORMAL, painter);
298   painter = views::Painter::CreateImageGridPainter(images[1]);
299   border->SetPainter(false, Button::STATE_HOVERED, painter);
300   painter = views::Painter::CreateImageGridPainter(images[2]);
301   border->SetPainter(false, Button::STATE_PRESSED, painter);
302
303   SetBorder(border.PassAs<views::Border>());
304
305   // Calculate a representative background color of the provided image grid and
306   // set it as the background color of the host label in order to color the text
307   // appropriately. We grab the color of the middle pixel of the middle image
308   // of the background, which we treat as the representative color of the entire
309   // background (reasonable, given the current appearance of these images).
310   const SkBitmap& bitmap(
311       GetThemeProvider()->GetImageSkiaNamed(
312           images[0][4])->GetRepresentation(1.0f).sk_bitmap());
313   SkAutoLockPixels pixel_lock(bitmap);
314   host_label_->SetBackgroundColor(
315       bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2));
316 }
317
318 void OriginChipView::AnimationProgressed(const gfx::Animation* animation) {
319   if (animation == fade_in_animation_.get())
320     SchedulePaint();
321   else
322     views::LabelButton::AnimationProgressed(animation);
323 }
324
325 void OriginChipView::AnimationEnded(const gfx::Animation* animation) {
326   if (animation == fade_in_animation_.get())
327     fade_in_animation_->Reset();
328   else
329     views::LabelButton::AnimationEnded(animation);
330 }
331
332 void OriginChipView::Layout() {
333   // TODO(gbillock): Eventually we almost certainly want to use
334   // LocationBarLayout for leading and trailing decorations.
335
336   location_icon_view_->SetBounds(
337       kEdgeThickness + (showing_16x16_icon_ ? k16x16IconLeadingSpacing : 0),
338       LocationBarView::kNormalEdgeThickness,
339       location_icon_view_->GetPreferredSize().width(),
340       height() - 2 * LocationBarView::kNormalEdgeThickness);
341
342   int host_label_x = location_icon_view_->x() + location_icon_view_->width() +
343       kIconTextSpacing;
344   host_label_x += showing_16x16_icon_ ? k16x16IconTrailingSpacing : 0;
345   int host_label_width =
346       width() - host_label_x - kEdgeThickness - kTrailingLabelMargin;
347   host_label_->SetBounds(host_label_x,
348                          LocationBarView::kNormalEdgeThickness,
349                          host_label_width,
350                          height() - 2 * LocationBarView::kNormalEdgeThickness);
351 }
352
353 void OriginChipView::OnPaintBorder(gfx::Canvas* canvas) {
354   if (fade_in_animation_->is_animating()) {
355     canvas->SaveLayerAlpha(static_cast<uint8>(
356         fade_in_animation_->CurrentValueBetween(0, 255)));
357     views::LabelButton::OnPaintBorder(canvas);
358     canvas->Restore();
359   } else {
360     views::LabelButton::OnPaintBorder(canvas);
361   }
362 }
363
364 // TODO(gbillock): Make the LocationBarView or OmniboxView the listener for
365 // this button.
366 void OriginChipView::ButtonPressed(views::Button* sender,
367                                  const ui::Event& event) {
368   // See if the event needs to be passed to the LocationIconView.
369   if (event.IsMouseEvent() || (event.type() == ui::ET_GESTURE_TAP)) {
370     location_icon_view_->set_interactive(true);
371     const ui::LocatedEvent& located_event =
372         static_cast<const ui::LocatedEvent&>(event);
373     if (GetEventHandlerForPoint(located_event.location()) ==
374             location_icon_view_) {
375       location_icon_view_->page_info_helper()->ProcessEvent(located_event);
376       location_icon_view_->set_interactive(false);
377       return;
378     }
379     location_icon_view_->set_interactive(false);
380   }
381
382   UMA_HISTOGRAM_COUNTS("OriginChip.Pressed", 1);
383   content::RecordAction(base::UserMetricsAction("OriginChipPress"));
384
385   location_bar_view_->ShowURL();
386 }
387
388 // Note: When OnSafeBrowsingHit would be called, OnSafeBrowsingMatch will
389 // have already been called.
390 void OriginChipView::OnSafeBrowsingHit(
391     const SafeBrowsingUIManager::UnsafeResource& resource) {}
392
393 void OriginChipView::OnSafeBrowsingMatch(
394     const SafeBrowsingUIManager::UnsafeResource& resource) {
395   OnChanged();
396 }