Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / location_bar / origin_chip_decoration.mm
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 #import "chrome/browser/ui/cocoa/location_bar/origin_chip_decoration.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/metrics/user_metrics_action.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/search/search.h"
12 #include "chrome/browser/extensions/extension_icon_image.h"
13 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
14 #include "chrome/browser/safe_browsing/ui_manager.h"
15 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
16 #import "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
17 #import "chrome/browser/ui/cocoa/nsview_additions.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/toolbar/origin_chip_info.h"
20 #include "content/public/browser/user_metrics.h"
21 #include "grit/theme_resources.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/image/image.h"
25
26 namespace {
27
28 const CGFloat kInnerLeftPadding = 3;
29 const CGFloat kIconLabelPadding = 2;
30 const CGFloat kInnerRightPadding = 6;
31 const CGFloat kOuterRightPadding = 3;
32
33 // The target icon size - smaller icons will be centered, and larger icons will
34 // be scaled down.
35 const CGFloat kIconSize = 19;
36
37 const ui::NinePartImageIds kNormalImages[3] = {
38   IMAGE_GRID(IDR_ORIGIN_CHIP_NORMAL),
39   IMAGE_GRID(IDR_ORIGIN_CHIP_HOVER),
40   IMAGE_GRID(IDR_ORIGIN_CHIP_PRESSED)
41 };
42
43 const ui::NinePartImageIds kMalwareImages[3] = {
44   IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_NORMAL),
45   IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_HOVER),
46   IMAGE_GRID(IDR_ORIGIN_CHIP_MALWARE_PRESSED)
47 };
48
49 const ui::NinePartImageIds kBrokenSSLImages[3] = {
50   IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_NORMAL),
51   IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_HOVER),
52   IMAGE_GRID(IDR_ORIGIN_CHIP_BROKENSSL_PRESSED)
53 };
54
55 const ui::NinePartImageIds kEVImages[3] = {
56   IMAGE_GRID(IDR_ORIGIN_CHIP_EV_NORMAL),
57   IMAGE_GRID(IDR_ORIGIN_CHIP_EV_HOVER),
58   IMAGE_GRID(IDR_ORIGIN_CHIP_EV_PRESSED)
59 };
60
61 }  // namespace
62
63 OriginChipDecoration::OriginChipDecoration(LocationBarViewMac* owner)
64     : ButtonDecoration(kNormalImages[0], IDR_LOCATION_BAR_HTTP,
65                        kNormalImages[1], IDR_LOCATION_BAR_HTTP,
66                        kNormalImages[2], IDR_LOCATION_BAR_HTTP, 0),
67       attributes_([[NSMutableDictionary alloc] init]),
68       info_(this, owner->browser()->profile()),
69       owner_(owner) {
70   DCHECK(owner_);
71   [attributes_ setObject:GetFont() forKey:NSFontAttributeName];
72
73   // May not be set for unit tests.
74   scoped_refptr<SafeBrowsingService> sb_service =
75       g_browser_process->safe_browsing_service();
76   if (sb_service.get() && sb_service->ui_manager())
77     sb_service->ui_manager()->AddObserver(this);
78 }
79
80 OriginChipDecoration::~OriginChipDecoration() {
81   scoped_refptr<SafeBrowsingService> sb_service =
82       g_browser_process->safe_browsing_service();
83   if (sb_service.get() && sb_service->ui_manager())
84     sb_service->ui_manager()->RemoveObserver(this);
85 }
86
87 void OriginChipDecoration::Update() {
88   if (!ShouldShow()) {
89     SetVisible(false);
90     return;
91   } else {
92     SetVisible(true);
93   }
94
95   const content::WebContents* web_contents =
96       owner_->browser()->tab_strip_model()->GetActiveWebContents();
97   if (!web_contents || !info_.Update(web_contents, owner_->GetToolbarModel()))
98     return;
99
100   if (info_.is_url_malware()) {
101     SetBackgroundImageIds(kMalwareImages[0],
102                           kMalwareImages[1],
103                           kMalwareImages[2]);
104   } else if (info_.security_level() == ToolbarModel::SECURITY_ERROR) {
105     SetBackgroundImageIds(kBrokenSSLImages[0],
106                           kBrokenSSLImages[1],
107                           kBrokenSSLImages[2]);
108   } else if (info_.security_level() == ToolbarModel::EV_SECURE) {
109     SetBackgroundImageIds(kEVImages[0],
110                           kEVImages[1],
111                           kEVImages[2]);
112   } else {
113     SetBackgroundImageIds(kNormalImages[0],
114                           kNormalImages[1],
115                           kNormalImages[2]);
116   }
117
118   NSColor* foreground_color =
119       info_.is_url_malware() ? [NSColor whiteColor] : [NSColor blackColor];
120   [attributes_ setObject:foreground_color
121                   forKey:NSForegroundColorAttributeName];
122
123   label_.reset([base::SysUTF16ToNSString(info_.label()) retain]);
124   SetIcon(info_.icon());
125 }
126
127 CGFloat OriginChipDecoration::GetWidthForSpace(CGFloat width) {
128   if (!GetIconImage() || [label_ length] == 0)
129     return kOmittedWidth;
130
131   // Clip the chip if it can't fit, rather than hiding it (kOmittedWidth).
132   return std::min(width, GetChipWidth() + kOuterRightPadding);
133 }
134
135 void OriginChipDecoration::DrawInFrame(NSRect frame, NSView* control_view) {
136   // The assets are aligned with the location bar assets, so check that we are
137   // being asked to draw against the edge, then draw over the border.
138   DCHECK(NSMinX(frame) == [control_view cr_lineWidth]);
139   frame = NSMakeRect(0, NSMinY(frame), GetChipWidth(), NSHeight(frame));
140
141   const ui::NinePartImageIds image_ids = GetBackgroundImageIds();
142
143   NSImage* icon =
144       extension_icon_.get() ? extension_icon_.get() : GetIconImage();
145   if (!icon)
146     return;
147
148   ui::DrawNinePartImage(frame, image_ids, NSCompositeSourceOver, 1.0, YES);
149
150   const CGFloat icon_height = std::min([icon size].height, kIconSize);
151   const CGFloat icon_width = std::min([icon size].width, kIconSize);
152   CGFloat icon_y_inset = (NSHeight(frame) - icon_height) / 2.0;
153   CGFloat icon_x_leading_offset = (kIconSize - icon_width) / 2.0;
154   CGFloat icon_x_trailing_offset = icon_x_leading_offset;
155
156   // Don't allow half-pixel offsets if we are not on a HiDPI display.
157   if ([control_view cr_lineWidth] == 1.0) {
158     icon_y_inset = std::ceil(icon_y_inset);
159     icon_x_leading_offset = std::ceil(icon_x_leading_offset);
160     icon_x_trailing_offset = std::floor(icon_x_trailing_offset);
161   }
162
163   NSRect icon_rect = NSMakeRect(kInnerLeftPadding + icon_x_leading_offset,
164                                 icon_y_inset, icon_width, icon_height);
165   [icon drawInRect:icon_rect
166           fromRect:NSZeroRect
167          operation:NSCompositeSourceOver
168           fraction:1.0
169     respectFlipped:YES
170              hints:nil];
171
172   NSRect label_rect =
173       NSMakeRect(NSMaxX(icon_rect) + icon_x_trailing_offset + kIconLabelPadding,
174                  0, [label_ sizeWithAttributes:attributes_].width,
175                  frame.size.height);
176   DrawLabel(label_, attributes_, label_rect);
177 }
178
179 NSString* OriginChipDecoration::GetToolTip() {
180   return base::SysUTF16ToNSString(info_.Tooltip());
181 }
182
183 bool OriginChipDecoration::OnMousePressed(NSRect frame) {
184   // TODO(macourteau): reveal the permissions bubble if the click was inside the
185   // icon's bounds - otherwise, show the URL.
186   UMA_HISTOGRAM_COUNTS("OriginChip.Pressed", 1);
187   content::RecordAction(base::UserMetricsAction("OriginChipPress"));
188   owner_->GetOmniboxView()->ShowURL();
189   return true;
190 }
191
192 void OriginChipDecoration::OnExtensionIconImageChanged(
193       extensions::IconImage* image) {
194   if (image) {
195     extension_icon_.reset(gfx::Image(image->image_skia()).CopyNSImage());
196     [owner_->GetAutocompleteTextField() setNeedsDisplay:YES];
197   } else {
198     extension_icon_.reset();
199   }
200 }
201
202 // Note: When OnSafeBrowsingHit would be called, OnSafeBrowsingMatch will
203 // have already been called.
204 void OriginChipDecoration::OnSafeBrowsingHit(
205     const SafeBrowsingUIManager::UnsafeResource& resource) {}
206
207 void OriginChipDecoration::OnSafeBrowsingMatch(
208     const SafeBrowsingUIManager::UnsafeResource& resource) {
209   Update();
210 }
211
212 bool OriginChipDecoration::ShouldShow() const {
213   return chrome::ShouldDisplayOriginChip() ||
214       (owner_->GetToolbarModel()->WouldOmitURLDueToOriginChip() &&
215        owner_->GetToolbarModel()->origin_chip_enabled());
216 }
217
218 CGFloat OriginChipDecoration::GetChipWidth() const {
219   return kInnerLeftPadding +
220       kIconSize +
221       kIconLabelPadding +
222       std::ceil([label_ sizeWithAttributes:attributes_].width) +
223       kInnerRightPadding;
224 }
225
226 // TODO(macourteau): Implement eliding of the host.
227
228 // TODO(macourteau): Implement dragging support.