Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / autofill / autofill_notification_controller.mm
blob01a29b3a91920163d99baff12728235e715ddb78
1 // Copyright 2013 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 #import "chrome/browser/ui/cocoa/autofill/autofill_notification_controller.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
14 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
15 #include "chrome/browser/ui/chrome_style.h"
16 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
17 #import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h"
18 #include "grit/theme_resources.h"
19 #include "skia/ext/skia_utils_mac.h"
20 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
22 @interface AutofillNotificationView : NSView {
23  @private
24   // Weak, determines anchor point for arrow.
25   NSView* arrowAnchorView_;
26   BOOL hasArrow_;
27   base::scoped_nsobject<NSColor> backgroundColor_;
28   base::scoped_nsobject<NSColor> borderColor_;
31 @property (nonatomic, assign) NSView* anchorView;
32 @property (nonatomic, assign) BOOL hasArrow;
33 @property (nonatomic, retain) NSColor* backgroundColor;
34 @property (nonatomic, retain) NSColor* borderColor;
36 @end
38 @implementation AutofillNotificationView
40 @synthesize hasArrow = hasArrow_;
41 @synthesize anchorView = arrowAnchorView_;
43 - (void)drawRect:(NSRect)dirtyRect {
44   [super drawRect:dirtyRect];
46   NSBezierPath* path;
47   NSRect bounds = [self bounds];
48   if (!hasArrow_) {
49     path = [NSBezierPath bezierPathWithRect:bounds];
50   } else {
51     // The upper tip of the arrow.
52     NSPoint anchorPoint = NSMakePoint(NSMidX([arrowAnchorView_ bounds]), 0);
53     anchorPoint = [self convertPoint:anchorPoint fromView:arrowAnchorView_];
54     anchorPoint.y = NSMaxY(bounds);
55     // The minimal rectangle that encloses the arrow.
56     NSRect arrowRect = NSMakeRect(anchorPoint.x - autofill::kArrowWidth / 2.0,
57                                   anchorPoint.y - autofill::kArrowHeight,
58                                   autofill::kArrowWidth,
59                                   autofill::kArrowHeight);
61     // Include the arrow and the rectangular non-arrow region in the same path,
62     // so that the stroke is easier to draw. Start at the upper-left of the
63     // rectangular region, and proceed clockwise.
64     path = [NSBezierPath bezierPath];
65     [path moveToPoint:NSMakePoint(NSMinX(bounds), NSMinY(arrowRect))];
66     [path lineToPoint:arrowRect.origin];
67     [path lineToPoint:NSMakePoint(NSMidX(arrowRect), NSMaxY(arrowRect))];
68     [path lineToPoint:NSMakePoint(NSMaxX(arrowRect), NSMinY(arrowRect))];
69     [path lineToPoint:NSMakePoint(NSMaxX(bounds), NSMinY(arrowRect))];
70     [path lineToPoint:NSMakePoint(NSMaxX(bounds), NSMinY(bounds))];
71     [path lineToPoint:NSMakePoint(NSMinX(bounds), NSMinY(bounds))];
72     [path closePath];
73   }
75   [backgroundColor_ setFill];
76   [path fill];
77   [borderColor_ setStroke];
78   [path stroke];
81 - (NSColor*)backgroundColor {
82   return backgroundColor_;
85 - (void)setBackgroundColor:(NSColor*)backgroundColor {
86   backgroundColor_.reset([backgroundColor retain]);
89 - (NSColor*)borderColor {
90   return borderColor_;
93 - (void)setBorderColor:(NSColor*)borderColor {
94   borderColor_.reset([borderColor retain]);
97 @end
99 @implementation AutofillNotificationController
101 - (id)initWithNotification:(const autofill::DialogNotification*)notification
102                   delegate:(autofill::AutofillDialogViewDelegate*)delegate {
103   if (self = [super init]) {
104     delegate_ = delegate;
105     notificationType_ = notification->type();
107     base::scoped_nsobject<AutofillNotificationView> view(
108         [[AutofillNotificationView alloc] initWithFrame:NSZeroRect]);
109     [view setBackgroundColor:
110         gfx::SkColorToCalibratedNSColor(notification->GetBackgroundColor())];
111     [view setBorderColor:
112         gfx::SkColorToCalibratedNSColor(notification->GetBorderColor())];
113     [self setView:view];
115     textview_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
116     NSColor* textColor =
117         gfx::SkColorToCalibratedNSColor(notification->GetTextColor());
118     [textview_ setMessage:base::SysUTF16ToNSString(notification->display_text())
119                  withFont:[NSFont labelFontOfSize:[[textview_ font] pointSize]]
120              messageColor:textColor];
121     if (!notification->link_range().is_empty()) {
122       // This class is not currently able to render links as checkbox labels.
123       DCHECK(!notification->HasCheckbox());
124       [textview_ setDelegate:self];
125       [textview_ addLinkRange:notification->link_range().ToNSRange()
126                      withName:self
127                     linkColor:[NSColor blueColor]];
128       linkURL_ = notification->link_url();
129     }
130     [textview_ setHidden:notification->HasCheckbox()];
132     checkbox_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
133     [checkbox_ setButtonType:NSSwitchButton];
134     [checkbox_ setHidden:!notification->HasCheckbox()];
135     [checkbox_ setState:(notification->checked() ? NSOnState : NSOffState)];
136     [checkbox_ setAttributedTitle:[textview_ textStorage]];
137     [checkbox_ setTarget:self];
138     [checkbox_ setAction:@selector(checkboxClicked:)];
139     // Set the size that preferredSizeForWidth will use. Do this here because
140     //   (1) preferredSizeForWidth is logically const, and so shouldn't have a
141     //       side-effect of updating the checkbox's frame, and
142     //   (2) this way, the sizing computation can be cached.
143     [checkbox_ sizeToFit];
145     tooltipController_.reset([[AutofillTooltipController alloc]
146                                  initWithArrowLocation:info_bubble::kTopRight]);
147     [tooltipController_ setImage:
148         ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
149             IDR_AUTOFILL_TOOLTIP_ICON).ToNSImage()];
150     [tooltipController_ setMessage:
151         base::SysUTF16ToNSString(notification->tooltip_text())];
152     [[tooltipController_ view] setHidden:
153         [[tooltipController_ message] length] == 0];
155     [view setSubviews:@[ textview_, checkbox_, [tooltipController_ view] ]];
156   }
157   return self;
160 - (AutofillNotificationView*)notificationView {
161   return base::mac::ObjCCastStrict<AutofillNotificationView>([self view]);
164 - (void)setHasArrow:(BOOL)hasArrow withAnchorView:(NSView*)anchorView {
165   [[self notificationView] setAnchorView:anchorView];
166   [[self notificationView] setHasArrow:hasArrow];
169 - (BOOL)hasArrow {
170   return [[self notificationView] hasArrow];
173 - (NSTextView*)textview {
174   return textview_;
177 - (NSButton*)checkbox {
178   return checkbox_;
181 - (NSView*)tooltipView {
182   return [tooltipController_ view];
185 - (NSSize)preferredSizeForWidth:(CGFloat)width {
186   width -= 2 * chrome_style::kHorizontalPadding;
187   if (![[tooltipController_ view] isHidden]) {
188     width -= NSWidth([[tooltipController_ view] frame]) +
189         chrome_style::kHorizontalPadding;
190   }
191   // TODO(isherman): Restore the DCHECK below once I figure out why it causes
192   // unit tests to fail.
193   //DCHECK_GT(width, 0);
195   NSSize preferredSize;
196   if (![textview_ isHidden]) {
197     // This method is logically const. Hence, cache the original frame so that
198     // it can be restored once the preferred size has been computed.
199     NSRect frame = [textview_ frame];
201     // Compute preferred size.
202     [textview_ setFrameSize:NSMakeSize(width, frame.size.height)];
203     [textview_ setVerticallyResizable:YES];
204     [textview_ sizeToFit];
205     preferredSize = [textview_ frame].size;
207     // Restore original properties, since this method is logically const.
208     [textview_ setFrame:frame];
209     [textview_ setVerticallyResizable:NO];
210   } else {
211     // Unlike textfields, checkboxes (NSButtons, really) are not designed to
212     // support multi-line labels. Hence, ignore the |width| and simply use the
213     // size that fits fit the checkbox's contents.
214     // NOTE: This logic will need to be updated if there is ever a need to
215     // support checkboxes with multi-line labels.
216     DCHECK(![checkbox_ isHidden]);
217     preferredSize = [checkbox_ frame].size;
218   }
220   if ([[self notificationView] hasArrow])
221       preferredSize.height += autofill::kArrowHeight;
223   preferredSize.height += 2 * autofill::kNotificationPadding;
224   return preferredSize;
227 - (NSSize)preferredSize {
228   NOTREACHED();
229   return NSZeroSize;
232 - (void)performLayout {
233   NSRect bounds = [[self view] bounds];
234   if ([[self notificationView] hasArrow])
235     bounds.size.height -= autofill::kArrowHeight;
237   // Calculate the frame size, leaving room for padding around the notification,
238   // as well as for the tooltip if it is visible.
239   NSRect labelFrame = NSInsetRect(bounds,
240                                  chrome_style::kHorizontalPadding,
241                                  autofill::kNotificationPadding);
242   NSView* tooltipView = [tooltipController_ view];
243   if (![tooltipView isHidden]) {
244     labelFrame.size.width -=
245         NSWidth([tooltipView frame]) + chrome_style::kHorizontalPadding;
246   }
248   NSView* label = [checkbox_ isHidden] ? textview_.get() : checkbox_.get();
249   [label setFrame:labelFrame];
251   if (![tooltipView isHidden]) {
252     NSPoint tooltipOrigin =
253         NSMakePoint(
254             NSMaxX(labelFrame) + chrome_style::kHorizontalPadding,
255             NSMidY(labelFrame) - (NSHeight([tooltipView frame]) / 2.0));
256     [tooltipView setFrameOrigin:tooltipOrigin];
257   }
260 - (IBAction)checkboxClicked:(id)sender {
261   DCHECK(sender == checkbox_.get());
262   BOOL isChecked = ([checkbox_ state] == NSOnState);
263   delegate_->NotificationCheckboxStateChanged(notificationType_, isChecked);
266 - (BOOL)textView:(NSTextView *)textView
267    clickedOnLink:(id)link
268          atIndex:(NSUInteger)charIndex {
269   delegate_->LinkClicked(linkURL_);
270   return YES;
273 @end