1 // Copyright (c) 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_section_container.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
13 #include "chrome/browser/ui/chrome_style.h"
14 #import "chrome/browser/ui/cocoa/autofill/autofill_pop_up_button.h"
15 #import "chrome/browser/ui/cocoa/autofill/autofill_section_view.h"
16 #import "chrome/browser/ui/cocoa/autofill/autofill_suggestion_container.h"
17 #import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
18 #import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h"
19 #import "chrome/browser/ui/cocoa/autofill/layout_view.h"
20 #include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
21 #import "chrome/browser/ui/cocoa/image_button_cell.h"
22 #import "chrome/browser/ui/cocoa/menu_button.h"
23 #include "components/autofill/core/browser/autofill_type.h"
24 #include "content/public/browser/native_web_keyboard_event.h"
25 #include "grit/theme_resources.h"
26 #import "ui/base/cocoa/menu_controller.h"
27 #include "ui/base/l10n/l10n_util_mac.h"
28 #include "ui/base/models/combobox_model.h"
29 #include "ui/base/resource/resource_bundle.h"
33 // Constants used for layouting controls. These variables are copied from
34 // "ui/views/layout/layout_constants.h".
36 // Horizontal spacing between controls that are logically related.
37 const int kRelatedControlHorizontalSpacing = 8;
39 // Vertical spacing between controls that are logically related.
40 const int kRelatedControlVerticalSpacing = 8;
42 // TODO(estade): pull out these constants, and figure out better values
43 // for them. Note: These are duplicated from Views code.
45 // Fixed width for the details section.
46 const int kDetailsWidth = 440;
48 // Top/bottom inset for contents of a detail section.
49 const size_t kDetailSectionInset = 10;
51 // Vertical padding around the section header.
52 const CGFloat kVerticalHeaderPadding = 6;
54 // If the Autofill data comes from a credit card, make sure to overwrite the
55 // CC comboboxes (even if they already have something in them). If the
56 // Autofill data comes from an AutofillProfile, leave the comboboxes alone.
57 // TODO(groby): This kind of logic should _really_ live on the delegate.
58 bool ShouldOverwriteComboboxes(autofill::DialogSection section,
59 autofill::ServerFieldType type) {
60 if (autofill::AutofillType(type).group() != autofill::CREDIT_CARD) {
64 if (section == autofill::SECTION_CC) {
68 return section == autofill::SECTION_CC_BILLING;
73 @interface AutofillSectionContainer ()
75 // An input field has been edited or activated - inform the delegate and
76 // possibly reset the validity of the input (if it's a textfield).
77 - (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field
80 // Convenience method to retrieve a field type via the control's tag.
81 - (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control;
83 // Find the DetailInput* associated with a field type.
84 - (const autofill::DetailInput*)detailInputForType:
85 (autofill::ServerFieldType)type;
87 // Takes an NSArray of controls and builds a FieldValueMap from them.
88 // Translates between Cocoa code and delegate, essentially.
89 // All controls must inherit from NSControl and conform to AutofillInputView.
90 - (void)fillDetailOutputs:(autofill::FieldValueMap*)outputs
91 fromControls:(NSArray*)controls;
93 // Updates input fields based on delegate status. If |shouldClobber| is YES,
94 // will clobber existing data and reset fields to the initial values.
95 - (void)updateAndClobber:(BOOL)shouldClobber;
97 // Return YES if this is a section that contains CC info. (And, more
98 // importantly, a potential CVV field)
99 - (BOOL)isCreditCardSection;
101 // Create properly styled label for section. Autoreleased.
102 - (NSTextField*)makeDetailSectionLabel:(NSString*)labelText;
104 // Create a button offering input suggestions.
105 - (MenuButton*)makeSuggestionButton;
107 // Create a view with all inputs requested by |delegate_| and resets |input_|.
108 - (void)makeInputControls;
110 // Refresh all field icons based on |delegate_| status.
111 - (void)updateFieldIcons;
113 // Refresh the enabled/disabled state of all input fields.
114 - (void)updateEditability;
118 @implementation AutofillSectionContainer
120 @synthesize section = section_;
121 @synthesize validationDelegate = validationDelegate_;
123 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate
124 forSection:(autofill::DialogSection)section {
125 if (self = [super init]) {
127 delegate_ = delegate;
132 - (void)getInputs:(autofill::FieldValueMap*)output {
133 [self fillDetailOutputs:output fromControls:[inputs_ subviews]];
136 // Note: This corresponds to Views' "UpdateDetailsGroupState".
137 - (void)modelChanged {
138 ui::MenuModel* suggestionModel = delegate_->MenuModelForSection(section_);
139 menuController_.reset([[MenuController alloc] initWithModel:suggestionModel
140 useWithPopUpButtonCell:YES]);
141 NSMenu* menu = [menuController_ menu];
143 const BOOL hasSuggestions = [menu numberOfItems] > 0;
144 [suggestButton_ setHidden:!hasSuggestions];
146 [suggestButton_ setAttachedMenu:menu];
148 [self updateSuggestionState];
150 // TODO(groby): "Save in Chrome" handling. http://crbug.com/306203.
152 if (![[self view] isHidden])
153 [self validateFor:autofill::VALIDATE_EDIT];
155 // Always request re-layout on state change.
156 id delegate = [[view_ window] windowController];
157 if ([delegate respondsToSelector:@selector(requestRelayout)])
158 [delegate performSelector:@selector(requestRelayout)];
162 [self makeInputControls];
164 base::string16 labelText = delegate_->LabelForSection(section_);
166 [[self makeDetailSectionLabel:base::SysUTF16ToNSString(labelText)]
169 suggestButton_.reset([[self makeSuggestionButton] retain]);
170 suggestContainer_.reset([[AutofillSuggestionContainer alloc] init]);
172 view_.reset([[AutofillSectionView alloc] initWithFrame:NSZeroRect]);
173 [self setView:view_];
175 @[label_, inputs_, [suggestContainer_ view], suggestButton_]];
176 if (tooltipController_) {
177 [view_ addSubview:[tooltipController_ view]
178 positioned:NSWindowAbove
182 if ([self isCreditCardSection]) {
183 // Credit card sections *MUST* have a CREDIT_CARD_VERIFICATION_CODE input.
184 DCHECK([self detailInputForType:autofill::CREDIT_CARD_VERIFICATION_CODE]);
185 [[suggestContainer_ inputField] setTag:
186 autofill::CREDIT_CARD_VERIFICATION_CODE];
187 [[suggestContainer_ inputField] setInputDelegate:self];
193 - (NSSize)preferredSize {
194 if ([view_ isHidden])
197 NSSize labelSize = [label_ frame].size; // Assumes sizeToFit was called.
198 CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
199 if (showSuggestions_)
200 controlHeight = [suggestContainer_ preferredSize].height;
202 return NSMakeSize(kDetailsWidth + 2 * chrome_style::kHorizontalPadding,
203 labelSize.height + kVerticalHeaderPadding +
204 controlHeight + 2 * kDetailSectionInset);
207 - (void)performLayout {
208 if ([view_ isHidden])
211 NSSize buttonSize = [suggestButton_ frame].size; // Assume sizeToFit.
212 NSSize labelSize = [label_ frame].size; // Assumes sizeToFit was called.
213 CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
214 if (showSuggestions_)
215 controlHeight = [suggestContainer_ preferredSize].height;
217 NSRect viewFrame = NSZeroRect;
218 viewFrame.size = [self preferredSize];
220 NSRect contentFrame = NSInsetRect(viewFrame,
221 chrome_style::kHorizontalPadding,
222 kDetailSectionInset);
223 NSRect controlFrame, labelFrame, buttonFrame;
225 // Label is top left, suggestion button is top right, controls are below that.
226 NSDivideRect(contentFrame, &labelFrame, &controlFrame,
227 kVerticalHeaderPadding + labelSize.height, NSMaxYEdge);
228 NSDivideRect(labelFrame, &buttonFrame, &labelFrame,
229 buttonSize.width, NSMaxXEdge);
231 labelFrame = NSOffsetRect(labelFrame, 0, kVerticalHeaderPadding);
232 labelFrame.size = labelSize;
234 buttonFrame = NSOffsetRect(buttonFrame, 0, 5);
235 buttonFrame.size = buttonSize;
237 if (showSuggestions_) {
238 [[suggestContainer_ view] setFrame:controlFrame];
239 [suggestContainer_ performLayout];
241 [inputs_ setFrame:controlFrame];
243 [label_ setFrame:labelFrame];
244 [suggestButton_ setFrame:buttonFrame];
245 [inputs_ setHidden:showSuggestions_];
246 [[suggestContainer_ view] setHidden:!showSuggestions_];
247 [view_ setFrameSize:viewFrame.size];
248 if (tooltipController_) {
249 [[tooltipController_ view] setHidden:showSuggestions_];
250 NSRect tooltipIconFrame = [tooltipField_ decorationFrame];
251 tooltipIconFrame.origin =
252 [[self view] convertPoint:tooltipIconFrame.origin
253 fromView:[tooltipField_ superview]];
254 [[tooltipController_ view] setFrame:tooltipIconFrame];
258 - (KeyEventHandled)keyEvent:(NSEvent*)event forInput:(id)sender {
259 content::NativeWebKeyboardEvent webEvent(event);
261 // Only handle keyDown, to handle key repeats without duplicates.
262 if (webEvent.type != content::NativeWebKeyboardEvent::RawKeyDown)
263 return kKeyEventNotHandled;
265 // Allow the delegate to intercept key messages.
266 if (delegate_->HandleKeyPressEventInInput(webEvent))
267 return kKeyEventHandled;
268 return kKeyEventNotHandled;
271 - (void)onMouseDown:(NSControl<AutofillInputField>*)field {
272 [self fieldEditedOrActivated:field edited:NO];
273 [validationDelegate_ updateMessageForField:field];
276 - (void)fieldBecameFirstResponder:(NSControl<AutofillInputField>*)field {
277 [validationDelegate_ updateMessageForField:field];
280 - (void)didChange:(id)sender {
281 [self fieldEditedOrActivated:sender edited:YES];
284 - (void)didEndEditing:(id)sender {
285 delegate_->FocusMoved();
286 [validationDelegate_ hideErrorBubble];
287 [self validateFor:autofill::VALIDATE_EDIT];
288 [self updateEditability];
291 - (void)updateSuggestionState {
292 const autofill::SuggestionState& suggestionState =
293 delegate_->SuggestionStateForSection(section_);
294 showSuggestions_ = suggestionState.visible;
296 if (!suggestionState.extra_text.empty()) {
297 NSString* extraText =
298 base::SysUTF16ToNSString(suggestionState.extra_text);
299 NSImage* extraIcon = suggestionState.extra_icon.AsNSImage();
300 [suggestContainer_ showInputField:extraText withIcon:extraIcon];
303 // NOTE: It's important to set the input field, if there is one, _before_
304 // setting the suggestion text, since the suggestion container needs to
305 // account for the input field's width when deciding which of the two string
306 // representations to use.
307 NSString* verticallyCompactText =
308 base::SysUTF16ToNSString(suggestionState.vertically_compact_text);
309 NSString* horizontallyCompactText =
310 base::SysUTF16ToNSString(suggestionState.horizontally_compact_text);
312 setSuggestionWithVerticallyCompactText:verticallyCompactText
313 horizontallyCompactText:horizontallyCompactText
314 icon:suggestionState.icon.AsNSImage()
315 maxWidth:kDetailsWidth];
317 [view_ setShouldHighlightOnHover:showSuggestions_];
318 if (showSuggestions_)
319 [view_ setClickTarget:suggestButton_];
321 [view_ setClickTarget:nil];
322 [view_ setHidden:!delegate_->SectionIsActive(section_)];
326 [self updateAndClobber:YES];
329 - (void)fillForType:(const autofill::ServerFieldType)type {
330 // Make sure to overwrite the originating input if it is a text field.
331 AutofillTextField* field =
332 base::mac::ObjCCast<AutofillTextField>([inputs_ viewWithTag:type]);
333 [field setFieldValue:@""];
335 if (ShouldOverwriteComboboxes(section_, type)) {
336 for (NSControl* control in [inputs_ subviews]) {
337 AutofillPopUpButton* popup =
338 base::mac::ObjCCast<AutofillPopUpButton>(control);
340 autofill::ServerFieldType fieldType =
341 [self fieldTypeForControl:popup];
342 if (autofill::AutofillType(fieldType).group() ==
343 autofill::CREDIT_CARD) {
344 ui::ComboboxModel* model =
345 delegate_->ComboboxModelForAutofillType(fieldType);
347 [popup selectItemAtIndex:model->GetDefaultIndex()];
353 [self updateAndClobber:NO];
356 - (BOOL)validateFor:(autofill::ValidationType)validationType {
357 NSArray* fields = nil;
358 if (!showSuggestions_) {
359 fields = [inputs_ subviews];
360 } else if ([self isCreditCardSection]) {
361 if (![[suggestContainer_ inputField] isHidden])
362 fields = @[ [suggestContainer_ inputField] ];
365 // Ensure only editable fields are validated.
366 fields = [fields filteredArrayUsingPredicate:
367 [NSPredicate predicateWithBlock:
368 ^BOOL(NSControl<AutofillInputField>* field, NSDictionary* bindings) {
369 return [field isEnabled];
372 autofill::FieldValueMap detailOutputs;
373 [self fillDetailOutputs:&detailOutputs fromControls:fields];
374 autofill::ValidityMessages messages = delegate_->InputsAreValid(
375 section_, detailOutputs);
377 for (NSControl<AutofillInputField>* input in fields) {
378 const autofill::ValidityMessage& message =
379 messages.GetMessageOrDefault([self fieldTypeForControl:input]);
380 if (validationType != autofill::VALIDATE_FINAL && !message.sure)
382 [input setValidityMessage:base::SysUTF16ToNSString(message.text)];
383 [validationDelegate_ updateMessageForField:input];
386 return !messages.HasErrors();
389 - (NSString*)suggestionText {
390 return showSuggestions_ ? [[suggestContainer_ inputField] stringValue] : nil;
393 - (void)addInputsToArray:(NSMutableArray*)array {
394 [array addObjectsFromArray:[inputs_ subviews]];
396 // Only credit card sections can have a suggestion input.
397 if ([self isCreditCardSection])
398 [array addObject:[suggestContainer_ inputField]];
401 #pragma mark Internal API for AutofillSectionContainer.
403 - (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field
404 edited:(BOOL)edited {
405 autofill::ServerFieldType type = [self fieldTypeForControl:field];
406 base::string16 fieldValue = base::SysNSStringToUTF16([field fieldValue]);
408 // Get the frame rectangle for the designated field, in screen coordinates.
409 NSRect textFrameInScreen = [field convertRect:[field bounds] toView:nil];
410 textFrameInScreen.origin =
411 [[field window] convertBaseToScreen:textFrameInScreen.origin];
413 // And adjust for gfx::Rect being flipped compared to OSX coordinates.
414 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
415 textFrameInScreen.origin.y =
416 NSMaxY([screen frame]) - NSMaxY(textFrameInScreen);
417 gfx::Rect textFrameRect(NSRectToCGRect(textFrameInScreen));
419 delegate_->UserEditedOrActivatedInput(section_,
426 AutofillTextField* textfield = base::mac::ObjCCast<AutofillTextField>(field);
430 // If the field is marked as invalid, check if the text is now valid. Many
431 // fields (i.e. CC#) are invalid for most of the duration of editing, so
432 // flagging them as invalid prematurely is not helpful. However, correcting a
433 // minor mistake (i.e. a wrong CC digit) should immediately result in
434 // validation - positive user feedback.
435 if ([textfield invalid] && edited) {
436 base::string16 message = delegate_->InputValidityMessage(section_,
439 [textfield setValidityMessage:base::SysUTF16ToNSString(message)];
440 [validationDelegate_ updateMessageForField:textfield];
442 // If the field transitioned from invalid to valid, re-validate the group,
443 // since inter-field checks become meaningful with valid fields.
444 if (![textfield invalid])
445 [self validateFor:autofill::VALIDATE_EDIT];
448 // Update the icon if necessary.
449 if (delegate_->FieldControlsIcons(type))
450 [self updateFieldIcons];
451 [self updateEditability];
454 - (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control {
455 DCHECK([control tag]);
456 return static_cast<autofill::ServerFieldType>([control tag]);
459 - (const autofill::DetailInput*)detailInputForType:
460 (autofill::ServerFieldType)type {
461 for (size_t i = 0; i < detailInputs_.size(); ++i) {
462 if (detailInputs_[i]->type == type)
463 return detailInputs_[i];
465 // TODO(groby): Needs to be NOTREACHED. Can't, due to the fact that tests
466 // blindly call setFieldValue:forType:, even for non-existing inputs.
470 - (void)fillDetailOutputs:(autofill::FieldValueMap*)outputs
471 fromControls:(NSArray*)controls {
472 for (NSControl<AutofillInputField>* input in controls) {
473 DCHECK([input isKindOfClass:[NSControl class]]);
474 DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]);
475 outputs->insert(std::make_pair(
476 [self fieldTypeForControl:input],
477 base::SysNSStringToUTF16([input fieldValue])));
481 - (NSTextField*)makeDetailSectionLabel:(NSString*)labelText {
482 base::scoped_nsobject<NSTextField> label([[NSTextField alloc] init]);
484 [[NSFontManager sharedFontManager] convertFont:[label font]
485 toHaveTrait:NSBoldFontMask]];
486 [label setStringValue:labelText];
487 [label setEditable:NO];
488 [label setBordered:NO];
489 [label setDrawsBackground:NO];
491 return label.autorelease();
494 - (void)updateAndClobber:(BOOL)shouldClobber {
496 [self makeInputControls];
498 const autofill::DetailInputs& updatedInputs =
499 delegate_->RequestedFieldsForSection(section_);
501 for (autofill::DetailInputs::const_iterator iter = updatedInputs.begin();
502 iter != updatedInputs.end();
504 NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type];
506 if ([field isDefault])
507 [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)];
509 [self updateFieldIcons];
512 [self updateEditability];
516 - (BOOL)isCreditCardSection {
517 return section_ == autofill::SECTION_CC ||
518 section_ == autofill::SECTION_CC_BILLING;
521 - (MenuButton*)makeSuggestionButton {
522 base::scoped_nsobject<MenuButton> button([[MenuButton alloc] init]);
524 [button setOpenMenuOnClick:YES];
525 [button setBordered:NO];
526 [button setShowsBorderOnlyWhileMouseInside:YES];
528 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
530 rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON).ToNSImage();
531 [[button cell] setImage:image
532 forButtonState:image_button_cell::kDefaultState];
533 image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_H).
535 [[button cell] setImage:image
536 forButtonState:image_button_cell::kHoverState];
537 image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_P).
539 [[button cell] setImage:image
540 forButtonState:image_button_cell::kPressedState];
541 image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_D).
543 [[button cell] setImage:image
544 forButtonState:image_button_cell::kDisabledState];
546 // ImageButtonCell's cellSize is not working. (http://crbug.com/298501)
547 [button setFrameSize:[image size]];
548 return button.autorelease();
551 // TODO(estade): we should be using Chrome-style constrained window padding
553 - (void)makeInputControls {
555 // When |inputs_| is replaced in response to a country change, there's a
556 // didEndEditing dispatched that segfaults or DCHECKS() as it's operating on
557 // stale input fields. Nil out the input delegate so this doesn't happen.
558 for (NSControl<AutofillInputField>* input in [inputs_ subviews]) {
559 [input setInputDelegate:nil];
563 detailInputs_.clear();
565 // Keep a list of weak pointers to DetailInputs.
566 const autofill::DetailInputs& inputs =
567 delegate_->RequestedFieldsForSection(section_);
569 // Reverse the order of all the inputs.
570 for (int i = inputs.size() - 1; i >= 0; --i) {
571 detailInputs_.push_back(&(inputs[i]));
574 // Then right the reversal in each row.
575 std::vector<const autofill::DetailInput*>::iterator it;
576 for (it = detailInputs_.begin(); it < detailInputs_.end(); ++it) {
577 std::vector<const autofill::DetailInput*>::iterator start = it;
578 while (it != detailInputs_.end() &&
579 (*it)->length != autofill::DetailInput::LONG) {
582 std::reverse(start, it);
585 base::scoped_nsobject<LayoutView> view([[LayoutView alloc] init]);
586 [view setLayoutManager:
587 scoped_ptr<SimpleGridLayout>(new SimpleGridLayout(view))];
588 SimpleGridLayout* layout = [view layoutManager];
590 int column_set_id = 0;
591 for (size_t i = 0; i < detailInputs_.size(); ++i) {
592 const autofill::DetailInput& input = *detailInputs_[i];
594 if (input.length == autofill::DetailInput::LONG)
598 input.length == autofill::DetailInput::NONE ? -1 : column_set_id;
600 ColumnSet* columnSet = layout->GetColumnSet(kColumnSetId);
602 // Create a new column set and row.
603 columnSet = layout->AddColumnSet(kColumnSetId);
604 if (i != 0 && kColumnSetId != -1)
605 layout->AddPaddingRow(kRelatedControlVerticalSpacing);
606 layout->StartRow(0, kColumnSetId);
608 // Add a new column to existing row.
609 columnSet->AddPaddingColumn(kRelatedControlHorizontalSpacing);
610 // Must explicitly skip the padding column since we've already started
612 layout->SkipColumns(1);
615 columnSet->AddColumn(input.expand_weight ? input.expand_weight : 1.0f);
617 ui::ComboboxModel* inputModel =
618 delegate_->ComboboxModelForAutofillType(input.type);
619 base::scoped_nsprotocol<NSControl<AutofillInputField>*> control;
621 base::scoped_nsobject<AutofillPopUpButton> popup(
622 [[AutofillPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]);
623 for (int i = 0; i < inputModel->GetItemCount(); ++i) {
624 [popup addItemWithTitle:
625 base::SysUTF16ToNSString(inputModel->GetItemAt(i))];
627 [popup setDefaultValue:base::SysUTF16ToNSString(
628 inputModel->GetItemAt(inputModel->GetDefaultIndex()))];
629 control.reset(popup.release());
631 base::scoped_nsobject<AutofillTextField> field(
632 [[AutofillTextField alloc] init]);
633 [[field cell] setPlaceholderString:
634 l10n_util::FixUpWindowsStyleLabel(input.placeholder_text)];
635 NSString* tooltipText =
636 base::SysUTF16ToNSString(delegate_->TooltipForField(input.type));
637 if ([tooltipText length] > 0) {
638 if (!tooltipController_) {
639 tooltipController_.reset(
640 [[AutofillTooltipController alloc]
641 initWithArrowLocation:info_bubble::kTopRight]);
643 tooltipField_ = field.get();
645 ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
646 IDR_AUTOFILL_TOOLTIP_ICON).ToNSImage();
647 [tooltipController_ setImage:icon];
648 [tooltipController_ setMessage:tooltipText];
649 [[field cell] setDecorationSize:[icon size]];
651 [field setDefaultValue:@""];
652 control.reset(field.release());
654 [control setFieldValue:base::SysUTF16ToNSString(input.initial_value)];
656 [control setFrame:NSIntegralRect([control frame])];
657 [control setTag:input.type];
658 [control setInputDelegate:self];
659 // Hide away fields that cannot be edited.
660 if (kColumnSetId == -1) {
661 [control setFrame:NSZeroRect];
662 [control setHidden:YES];
664 layout->AddView(control);
666 if (input.length == autofill::DetailInput::LONG ||
667 input.length == autofill::DetailInput::SHORT_EOL) {
673 [[self view] replaceSubview:inputs_ with:view];
674 id delegate = [[view_ window] windowController];
675 if ([delegate respondsToSelector:@selector(requestRelayout)])
676 [delegate performSelector:@selector(requestRelayout)];
680 [self updateFieldIcons];
683 - (void)updateFieldIcons {
684 autofill::FieldValueMap fieldValues;
685 for (NSControl<AutofillInputField>* input in [inputs_ subviews]) {
686 DCHECK([input isKindOfClass:[NSControl class]]);
687 DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]);
688 autofill::ServerFieldType fieldType = [self fieldTypeForControl:input];
689 NSString* value = [input fieldValue];
690 fieldValues[fieldType] = base::SysNSStringToUTF16(value);
693 autofill::FieldIconMap fieldIcons = delegate_->IconsForFields(fieldValues);
694 for (autofill::FieldIconMap::const_iterator iter = fieldIcons.begin();
695 iter!= fieldIcons.end(); ++iter) {
696 AutofillTextField* textfield = base::mac::ObjCCastStrict<AutofillTextField>(
697 [inputs_ viewWithTag:iter->first]);
698 [[textfield cell] setIcon:iter->second.ToNSImage()];
702 - (void)updateEditability {
703 base::scoped_nsobject<NSMutableArray> controls([[NSMutableArray alloc] init]);
704 [self addInputsToArray:controls];
705 for (NSControl<AutofillInputField>* control in controls.get()) {
706 autofill::ServerFieldType type = [self fieldTypeForControl:control];
707 const autofill::DetailInput* input = [self detailInputForType:type];
708 [control setEnabled:delegate_->InputIsEditable(*input, section_)];
715 @implementation AutofillSectionContainer (ForTesting)
717 - (NSControl*)getField:(autofill::ServerFieldType)type {
718 return [inputs_ viewWithTag:type];
721 - (void)setFieldValue:(NSString*)text
722 forType:(autofill::ServerFieldType)type {
723 NSControl<AutofillInputField>* field = [inputs_ viewWithTag:type];
725 [field setFieldValue:text];
728 - (void)setSuggestionFieldValue:(NSString*)text {
729 [[suggestContainer_ inputField] setFieldValue:text];
732 - (void)activateFieldForType:(autofill::ServerFieldType)type {
733 NSControl<AutofillInputField>* field = [inputs_ viewWithTag:type];
735 [[field window] makeFirstResponder:field];
736 [self fieldEditedOrActivated:field edited:NO];