Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / autofill / password_generation_popup_view_cocoa.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/autofill/password_generation_popup_view_cocoa.h"
6
7 #include <cmath>
8
9 #include "base/logging.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
12 #include "chrome/browser/ui/autofill/autofill_popup_view.h"
13 #include "chrome/browser/ui/autofill/popup_constants.h"
14 #include "chrome/browser/ui/chrome_style.h"
15 #include "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_bridge.h"
16 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
17 #import "chrome/browser/ui/cocoa/l10n_util.h"
18 #include "components/autofill/core/browser/popup_item_ids.h"
19 #include "grit/theme_resources.h"
20 #include "skia/ext/skia_utils_mac.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/gfx/font_list.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/gfx/point.h"
25 #include "ui/gfx/range/range.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/text_constants.h"
28
29 using autofill::AutofillPopupView;
30 using autofill::PasswordGenerationPopupController;
31 using autofill::PasswordGenerationPopupView;
32 using base::scoped_nsobject;
33
34 namespace {
35
36 // The height of the divider between the password and help sections, in pixels.
37 const CGFloat kDividerHeight = 1;
38
39 // The amount of whitespace, in pixels, between lines of text in the password
40 // section.
41 const CGFloat kPasswordSectionVerticalSeparation = 5;
42
43 NSColor* DividerColor() {
44   return gfx::SkColorToCalibratedNSColor(
45       PasswordGenerationPopupView::kDividerColor);
46 }
47
48 NSColor* HelpTextBackgroundColor() {
49   return gfx::SkColorToCalibratedNSColor(
50       PasswordGenerationPopupView::kExplanatoryTextBackgroundColor);
51 }
52
53 NSColor* HelpTextColor() {
54   return gfx::SkColorToCalibratedNSColor(
55       PasswordGenerationPopupView::kExplanatoryTextColor);
56 }
57
58 NSColor* HelpLinkColor() {
59   return gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
60 }
61
62 }  // namespace
63
64 @implementation PasswordGenerationPopupViewCocoa
65
66 #pragma mark Initialisers
67
68 - (id)initWithFrame:(NSRect)frame {
69   NOTREACHED();
70   return nil;
71 }
72
73 - (id)initWithController:
74     (autofill::PasswordGenerationPopupController*)controller
75                    frame:(NSRect)frame {
76   if (self = [super initWithDelegate:controller frame:frame]) {
77     controller_ = controller;
78
79     passwordSection_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
80     [self addSubview:passwordSection_];
81
82     passwordField_.reset(
83         [[self textFieldWithText:controller_->password()
84                       attributes:[self passwordAttributes]] retain]);
85     [passwordSection_ addSubview:passwordField_];
86
87     passwordTitleField_.reset(
88         [[self textFieldWithText:controller_->SuggestedText()
89                       attributes:[self passwordTitleAttributes]] retain]);
90     [passwordSection_ addSubview:passwordTitleField_];
91
92     keyIcon_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
93     NSImage* keyImage = ResourceBundle::GetSharedInstance()
94         .GetImageNamed(IDR_GENERATE_PASSWORD_KEY)
95         .ToNSImage();
96     [keyIcon_ setImage:keyImage];
97     [passwordSection_ addSubview:keyIcon_];
98
99     divider_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
100     [divider_ setBoxType:NSBoxCustom];
101     [divider_ setBorderType:NSLineBorder];
102     [divider_ setBorderColor:DividerColor()];
103     [self addSubview:divider_];
104
105     helpTextView_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
106     [helpTextView_ setMessage:base::SysUTF16ToNSString(controller_->HelpText())
107                      withFont:[self textFont]
108                  messageColor:HelpTextColor()];
109     [helpTextView_ addLinkRange:controller_->HelpTextLinkRange().ToNSRange()
110                        withName:@""
111                       linkColor:HelpLinkColor()];
112     [helpTextView_ setDelegate:self];
113     [helpTextView_ setDrawsBackground:YES];
114     [helpTextView_ setBackgroundColor:HelpTextBackgroundColor()];
115     [helpTextView_
116         setTextContainerInset:NSMakeSize(controller_->kHorizontalPadding,
117                                          controller_->kHelpVerticalPadding)];
118     // Remove the underlining.
119     NSTextStorage* text = [helpTextView_ textStorage];
120     [text addAttribute:NSUnderlineStyleAttributeName
121                  value:@(NSUnderlineStyleNone)
122                  range:controller_->HelpTextLinkRange().ToNSRange()];
123     [self addSubview:helpTextView_];
124 }
125
126   return self;
127 }
128
129 #pragma mark NSView implementation:
130
131 - (void)drawRect:(NSRect)dirtyRect {
132   [super drawRect:dirtyRect];
133
134   // If the view is in the process of being destroyed, don't bother drawing.
135   if (!controller_)
136     return;
137
138   [self drawBackgroundAndBorder];
139
140   if (controller_->password_selected()) {
141     // Draw a highlight under the suggested password.
142     NSRect highlightBounds = [passwordSection_ frame];
143     [[self highlightColor] set];
144     [NSBezierPath fillRect:highlightBounds];
145   }
146 }
147
148 #pragma mark Public API:
149
150 - (NSSize)preferredSize {
151   const NSSize passwordTitleSize =
152       [base::SysUTF16ToNSString(controller_->SuggestedText())
153           sizeWithAttributes:@{ NSFontAttributeName : [self boldFont] }];
154   const NSSize passwordSize = [base::SysUTF16ToNSString(controller_->password())
155       sizeWithAttributes:@{ NSFontAttributeName : [self textFont] }];
156
157   CGFloat width =
158       autofill::kPopupBorderThickness +
159       controller_->kHorizontalPadding +
160       [[keyIcon_ image] size].width +
161       controller_->kHorizontalPadding +
162       std::max(passwordSize.width, passwordTitleSize.width) +
163       controller_->kHorizontalPadding +
164       autofill::kPopupBorderThickness;
165
166   width = std::max(width, (CGFloat)controller_->GetMinimumWidth());
167
168   CGFloat height =
169       autofill::kPopupBorderThickness +
170       controller_->kHelpVerticalPadding +
171       [self helpSizeForPopupWidth:width].height +
172       controller_->kHelpVerticalPadding +
173       autofill::kPopupBorderThickness;
174
175   if (controller_->display_password())
176     height += controller_->kPopupPasswordSectionHeight;
177
178   return NSMakeSize(width, height);
179 }
180
181 - (void)updateBoundsAndRedrawPopup {
182   const CGFloat popupWidth = controller_->popup_bounds().width();
183   const CGFloat contentWidth =
184       popupWidth - (2 * autofill::kPopupBorderThickness);
185   const CGFloat contentHeight = controller_->popup_bounds().height() -
186                                 (2 * autofill::kPopupBorderThickness);
187
188   if (controller_->display_password()) {
189     // The password can change while the bubble is shown: If the user has
190     // accepted the password and then selects the form again and starts deleting
191     // the password, the field will be initially invisible and then become
192     // visible.
193     [self updatePassword];
194
195     // Lay out the password section, which includes the key icon, the title, and
196     // the suggested password.
197     [passwordSection_
198         setFrame:NSMakeRect(autofill::kPopupBorderThickness,
199                             autofill::kPopupBorderThickness,
200                             contentWidth,
201                             controller_->kPopupPasswordSectionHeight)];
202
203     // The key icon falls to the left of the title and password.
204     const NSSize imageSize = [[keyIcon_ image] size];
205     const CGFloat keyX = controller_->kHorizontalPadding;
206     const CGFloat keyY =
207         std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) -
208                   (imageSize.height / 2.0));
209     [keyIcon_ setFrame:{ NSMakePoint(keyX, keyY), imageSize }];
210
211     // The title and password fall to the right of the key icon and are centered
212     // vertically as a group with some padding in between.
213     [passwordTitleField_ sizeToFit];
214     [passwordField_ sizeToFit];
215     const CGFloat groupHeight = NSHeight([passwordField_ frame]) +
216                                 kPasswordSectionVerticalSeparation +
217                                 NSHeight([passwordTitleField_ frame]);
218     const CGFloat groupX =
219         NSMaxX([keyIcon_ frame]) + controller_->kHorizontalPadding;
220     const CGFloat groupY =
221         std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) -
222                   (groupHeight / 2.0));
223     [passwordField_ setFrameOrigin:NSMakePoint(groupX, groupY)];
224     const CGFloat titleY = groupY +
225                            NSHeight([passwordField_ frame]) +
226                            kPasswordSectionVerticalSeparation;
227     [passwordTitleField_ setFrameOrigin:NSMakePoint(groupX, titleY)];
228
229     // Layout the divider, which falls immediately below the password section.
230     const CGFloat dividerX = autofill::kPopupBorderThickness;
231     const CGFloat dividerY = NSMaxY([passwordSection_ frame]);
232     NSRect dividerFrame =
233         NSMakeRect(dividerX, dividerY, contentWidth, kDividerHeight);
234     [divider_ setFrame:dividerFrame];
235   }
236
237   // Layout the help section beneath the divider (if applicable, otherwise
238   // beneath the border).
239   const CGFloat helpX = autofill::kPopupBorderThickness;
240   const CGFloat helpY = controller_->display_password()
241       ? NSMaxY([divider_ frame])
242       : autofill::kPopupBorderThickness;
243   const CGFloat helpHeight = contentHeight -
244                              NSHeight([passwordSection_ frame]) -
245                              NSHeight([divider_ frame]);
246   [helpTextView_ setFrame:NSMakeRect(helpX, helpY, contentWidth, helpHeight)];
247
248   [super updateBoundsAndRedrawPopup];
249 }
250
251 - (BOOL)isPointInPasswordBounds:(NSPoint)point {
252   return NSPointInRect(point, [passwordSection_ frame]);
253 }
254
255 - (void)controllerDestroyed {
256   controller_ = NULL;
257   [super delegateDestroyed];
258 }
259
260 #pragma mark NSTextViewDelegate implementation:
261
262 - (BOOL)textView:(NSTextView*)textView
263    clickedOnLink:(id)link
264          atIndex:(NSUInteger)charIndex {
265   controller_->OnSavedPasswordsLinkClicked();
266   return YES;
267 }
268
269 #pragma mark Private helpers:
270
271 - (void)updatePassword {
272   base::scoped_nsobject<NSMutableAttributedString> updatedPassword(
273       [[NSMutableAttributedString alloc]
274           initWithString:base::SysUTF16ToNSString(controller_->password())
275               attributes:[self passwordAttributes]]);
276   [passwordField_ setAttributedStringValue:updatedPassword];
277 }
278
279 - (NSDictionary*)passwordTitleAttributes {
280   scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
281       [[NSMutableParagraphStyle alloc] init]);
282   [paragraphStyle setAlignment:NSLeftTextAlignment];
283   return @{
284     NSFontAttributeName : [self boldFont],
285     NSForegroundColorAttributeName : [self nameColor],
286     NSParagraphStyleAttributeName : paragraphStyle.autorelease()
287   };
288 }
289
290 - (NSDictionary*)passwordAttributes {
291   scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
292       [[NSMutableParagraphStyle alloc] init]);
293   [paragraphStyle setAlignment:NSLeftTextAlignment];
294   return @{
295     NSFontAttributeName : [self textFont],
296     NSForegroundColorAttributeName : [self nameColor],
297     NSParagraphStyleAttributeName : paragraphStyle.autorelease()
298   };
299 }
300
301 - (NSTextField*)textFieldWithText:(const base::string16&)text
302                        attributes:(NSDictionary*)attributes {
303   NSTextField* textField =
304       [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease];
305   scoped_nsobject<NSAttributedString> attributedString(
306       [[NSAttributedString alloc]
307           initWithString:base::SysUTF16ToNSString(text)
308               attributes:attributes]);
309   [textField setAttributedStringValue:attributedString.autorelease()];
310   [textField setEditable:NO];
311   [textField setSelectable:NO];
312   [textField setDrawsBackground:NO];
313   [textField setBezeled:NO];
314   return textField;
315 }
316
317 - (NSSize)helpSizeForPopupWidth:(CGFloat)width {
318   const CGFloat helpWidth = width -
319                             2 * controller_->kHorizontalPadding -
320                             2 * autofill::kPopupBorderThickness;
321   const NSSize size = NSMakeSize(helpWidth, MAXFLOAT);
322   NSRect textFrame = [base::SysUTF16ToNSString(controller_->HelpText())
323       boundingRectWithSize:size
324                    options:NSLineBreakByWordWrapping |
325                            NSStringDrawingUsesLineFragmentOrigin
326                 attributes:@{ NSFontAttributeName : [self textFont] }];
327   return textFrame.size;
328 }
329
330 - (NSFont*)boldFont {
331   return [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
332 }
333
334 - (NSFont*)textFont {
335   return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
336 }
337
338 @end