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