[rAC, OSX] Validate only enabled fields.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / autofill / autofill_section_container.mm
blobc1e64c784e50da48ebb06caa4c798433d2e1edd5
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"
7 #include <algorithm>
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 #import "chrome/browser/ui/cocoa/autofill/autofill_pop_up_button.h"
14 #import "chrome/browser/ui/cocoa/autofill/autofill_section_view.h"
15 #import "chrome/browser/ui/cocoa/autofill/autofill_suggestion_container.h"
16 #import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
17 #import "chrome/browser/ui/cocoa/autofill/layout_view.h"
18 #include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
19 #import "chrome/browser/ui/cocoa/image_button_cell.h"
20 #import "chrome/browser/ui/cocoa/menu_button.h"
21 #include "components/autofill/core/browser/autofill_type.h"
22 #include "grit/theme_resources.h"
23 #import "ui/base/cocoa/menu_controller.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
25 #include "ui/base/models/combobox_model.h"
26 #include "ui/base/resource/resource_bundle.h"
28 namespace {
30 // Constants used for layouting controls. These variables are copied from
31 // "ui/views/layout/layout_constants.h".
33 // Horizontal spacing between controls that are logically related.
34 const int kRelatedControlHorizontalSpacing = 8;
36 // Vertical spacing between controls that are logically related.
37 const int kRelatedControlVerticalSpacing = 8;
39 // TODO(estade): pull out these constants, and figure out better values
40 // for them. Note: These are duplicated from Views code.
42 // Fixed width for the section label.
43 const int kLabelWidth = 180;
45 // Padding between section label and section input.
46 const int kPadding = 30;
48 // Fixed width for the details section.
49 const int kDetailsWidth = 440;
51 // Top/bottom inset for contents of a detail section.
52 const size_t kDetailSectionInset = 10;
54 // Vertical padding around the section header.
55 const CGFloat kVerticalHeaderPadding = 6;
57 // Break suggestion text into two lines. TODO(groby): Should be on delegate.
58 void BreakSuggestionText(const string16& text,
59                          string16* line1,
60                          string16* line2) {
61   // TODO(estade): does this localize well?
62   string16 line_return(base::ASCIIToUTF16("\n"));
63   size_t position = text.find(line_return);
64   if (position == string16::npos) {
65     *line1 = text;
66     line2->clear();
67   } else {
68     *line1 = text.substr(0, position);
69     *line2 = text.substr(position + line_return.length());
70   }
73 // If the Autofill data comes from a credit card, make sure to overwrite the
74 // CC comboboxes (even if they already have something in them). If the
75 // Autofill data comes from an AutofillProfile, leave the comboboxes alone.
76 // TODO(groby): This kind of logic should _really_ live on the delegate.
77 bool ShouldOverwriteComboboxes(autofill::DialogSection section,
78                                autofill::ServerFieldType type) {
79   if (autofill::AutofillType(type).group() != autofill::CREDIT_CARD) {
80     return false;
81   }
83   if (section == autofill::SECTION_CC) {
84     return true;
85   }
87   return section == autofill::SECTION_CC_BILLING;
90 bool CompareInputRows(const autofill::DetailInput* input1,
91                       const autofill::DetailInput* input2) {
92   // Row ID -1 is sorted to the end of rows.
93   if (input2->row_id == -1)
94     return false;
95   return input2->row_id < input1->row_id;
100 @interface AutofillSectionContainer ()
102 // A text field has been edited or activated - inform the delegate that it's
103 // time to show a suggestion popup & possibly reset the validity of the input.
104 - (void)textfieldEditedOrActivated:(NSControl<AutofillInputField>*)field
105                             edited:(BOOL)edited;
107 // Convenience method to retrieve a field type via the control's tag.
108 - (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control;
110 // Find the DetailInput* associated with a field type.
111 - (const autofill::DetailInput*)detailInputForType:
112     (autofill::ServerFieldType)type;
114 // Takes an NSArray of controls and builds a DetailOutputMap from them.
115 // Translates between Cocoa code and delegate, essentially.
116 // All controls must inherit from NSControl and conform to AutofillInputView.
117 - (void)fillDetailOutputs:(autofill::DetailOutputMap*)outputs
118              fromControls:(NSArray*)controls;
120 // Updates input fields based on delegate status. If |shouldClobber| is YES,
121 // will clobber existing data and reset fields to the initial values.
122 - (void)updateAndClobber:(BOOL)shouldClobber;
124 // Create properly styled label for section. Autoreleased.
125 - (NSTextField*)makeDetailSectionLabel:(NSString*)labelText;
127 // Create a button offering input suggestions.
128 - (MenuButton*)makeSuggestionButton;
130 // Create a view with all inputs requested by |delegate_|. Autoreleased.
131 - (LayoutView*)makeInputControls;
133 @end
135 @implementation AutofillSectionContainer
137 @synthesize section = section_;
138 @synthesize validationDelegate = validationDelegate_;
140 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate
141               forSection:(autofill::DialogSection)section {
142   if (self = [super init]) {
143     section_ = section;
144     delegate_ = delegate;
145   }
146   return self;
149 - (void)getInputs:(autofill::DetailOutputMap*)output {
150   [self fillDetailOutputs:output fromControls:[inputs_ subviews]];
153 // Note: This corresponds to Views' "UpdateDetailsGroupState".
154 - (void)modelChanged {
155   ui::MenuModel* suggestionModel = delegate_->MenuModelForSection(section_);
156   menuController_.reset([[MenuController alloc] initWithModel:suggestionModel
157                                        useWithPopUpButtonCell:YES]);
158   NSMenu* menu = [menuController_ menu];
160   const BOOL hasSuggestions = [menu numberOfItems] > 0;
161   [suggestButton_ setHidden:!hasSuggestions];
163   [suggestButton_ setAttachedMenu:menu];
165   [self updateSuggestionState];
167   // TODO(groby): "Save in Chrome" handling.
169   if (![[self view] isHidden])
170     [self validateFor:autofill::VALIDATE_EDIT];
172   // Always request re-layout on state change.
173   id delegate = [[view_ window] windowController];
174   if ([delegate respondsToSelector:@selector(requestRelayout)])
175     [delegate performSelector:@selector(requestRelayout)];
178 - (void)loadView {
179   // Keep a list of weak pointers to DetailInputs.
180   const autofill::DetailInputs& inputs =
181       delegate_->RequestedFieldsForSection(section_);
182   for (size_t i = 0; i < inputs.size(); ++i) {
183     detailInputs_.push_back(&(inputs[i]));
184   }
186   inputs_.reset([[self makeInputControls] retain]);
187   string16 labelText = delegate_->LabelForSection(section_);
188   label_.reset([[self makeDetailSectionLabel:
189                    base::SysUTF16ToNSString(labelText)] retain]);
191   suggestButton_.reset([[self makeSuggestionButton] retain]);
192   suggestContainer_.reset([[AutofillSuggestionContainer alloc] init]);
194   view_.reset([[AutofillSectionView alloc] initWithFrame:NSZeroRect]);
195   [self setView:view_];
196   [[self view] setSubviews:
197       @[label_, inputs_, [suggestContainer_ view], suggestButton_]];
199   if (section_ == autofill::SECTION_CC) {
200     // SECTION_CC *MUST* have a CREDIT_CARD_VERIFICATION_CODE input.
201     DCHECK([self detailInputForType:autofill::CREDIT_CARD_VERIFICATION_CODE]);
202     [[suggestContainer_ inputField] setTag:
203         autofill::CREDIT_CARD_VERIFICATION_CODE];
204     [[suggestContainer_ inputField] setDelegate:self];
205   }
207   [self modelChanged];
210 - (NSSize)preferredSize {
211   if ([view_ isHidden])
212     return NSZeroSize;
214   NSSize labelSize = [label_ frame].size;  // Assumes sizeToFit was called.
215   CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
216   if (showSuggestions_)
217     controlHeight = [suggestContainer_ preferredSize].height;
219   return NSMakeSize(kDetailsWidth,
220                     labelSize.height + kVerticalHeaderPadding +
221                         controlHeight + 2 * kDetailSectionInset);
224 - (void)performLayout {
225   if ([view_ isHidden])
226     return;
228   NSSize buttonSize = [suggestButton_ frame].size;  // Assume sizeToFit.
229   NSSize labelSize = [label_ frame].size;  // Assumes sizeToFit was called.
230   CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
231   if (showSuggestions_)
232     controlHeight = [suggestContainer_ preferredSize].height;
234   NSRect viewFrame = NSZeroRect;
235   viewFrame.size = [self preferredSize];
237   NSRect contentFrame = NSInsetRect(viewFrame, 0, kDetailSectionInset);
238   NSRect controlFrame, labelFrame, buttonFrame;
240   // Label is top left, suggestion button is top right, controls are below that.
241   NSDivideRect(contentFrame, &labelFrame, &controlFrame,
242                kVerticalHeaderPadding + labelSize.height, NSMaxYEdge);
243   NSDivideRect(labelFrame, &buttonFrame, &labelFrame,
244                buttonSize.width, NSMaxXEdge);
246   labelFrame = NSOffsetRect(labelFrame, 0, kVerticalHeaderPadding);
247   labelFrame.size = labelSize;
249   buttonFrame = NSOffsetRect(buttonFrame, 0, 5);
250   buttonFrame.size = buttonSize;
252   if (showSuggestions_) {
253     [[suggestContainer_ view] setFrame:controlFrame];
254     [suggestContainer_ performLayout];
255   } else {
256     [inputs_ setFrame:controlFrame];
257   }
258   [label_ setFrame:labelFrame];
259   [suggestButton_ setFrame:buttonFrame];
260   [inputs_ setHidden:showSuggestions_];
261   [[suggestContainer_ view] setHidden:!showSuggestions_];
262   [view_ setFrameSize:viewFrame.size];
265 - (void)fieldBecameFirstResponder:(NSControl<AutofillInputField>*)field {
266   [self textfieldEditedOrActivated:field edited:NO];
267   [validationDelegate_ updateMessageForField:field];
270 - (void)didChange:(id)sender {
271   [self textfieldEditedOrActivated:sender edited:YES];
274 - (void)didEndEditing:(id)sender {
275   [self validateFor:autofill::VALIDATE_EDIT];
278 - (void)updateSuggestionState {
279   const autofill::SuggestionState& suggestionState =
280       delegate_->SuggestionStateForSection(section_);
281   // TODO(estade): use |vertically_compact_text| when it fits.
282   const base::string16& text = suggestionState.horizontally_compact_text;
283   showSuggestions_ = suggestionState.visible;
285   base::string16 line1;
286   base::string16 line2;
287   BreakSuggestionText(text, &line1, &line2);
288   [suggestContainer_ setSuggestionText:base::SysUTF16ToNSString(line1)
289                                  line2:base::SysUTF16ToNSString(line2)];
290   [suggestContainer_ setIcon:suggestionState.icon.AsNSImage()];
291   if (!suggestionState.extra_text.empty()) {
292     NSString* extraText =
293         base::SysUTF16ToNSString(suggestionState.extra_text);
294     NSImage* extraIcon = suggestionState.extra_icon.AsNSImage();
295     [suggestContainer_ showInputField:extraText withIcon:extraIcon];
296   }
297   [view_ setShouldHighlightOnHover:showSuggestions_];
298   if (showSuggestions_)
299     [view_ setClickTarget:suggestButton_];
300   else
301     [view_ setClickTarget:nil];
302   [view_ setHidden:!delegate_->SectionIsActive(section_)];
305 - (void)update {
306   [self updateAndClobber:YES];
309 - (void)fillForInput:(const autofill::DetailInput&)input {
310   // Make sure to overwrite the originating input if it is a text field.
311   AutofillTextField* field =
312       base::mac::ObjCCast<AutofillTextField>([inputs_ viewWithTag:input.type]);
313   [field setFieldValue:@""];
315   if (ShouldOverwriteComboboxes(section_, input.type)) {
316     for (NSControl* control in [inputs_ subviews]) {
317       AutofillPopUpButton* popup =
318           base::mac::ObjCCast<AutofillPopUpButton>(control);
319       if (popup) {
320         autofill::ServerFieldType fieldType =
321             [self fieldTypeForControl:popup];
322         if (autofill::AutofillType(fieldType).group() ==
323                 autofill::CREDIT_CARD) {
324           ui::ComboboxModel* model =
325               delegate_->ComboboxModelForAutofillType(fieldType);
326           DCHECK(model);
327           [popup selectItemAtIndex:model->GetDefaultIndex()];
328         }
329       }
330     }
331   }
333   [self updateAndClobber:NO];
336 - (BOOL)validateFor:(autofill::ValidationType)validationType {
337   NSArray* fields = nil;
338   if (![inputs_ isHidden]) {
339     fields = [inputs_ subviews];
340   } else if (section_ == autofill::SECTION_CC) {
341     fields = @[ [suggestContainer_ inputField] ];
342   }
344   // Ensure only editable fields are validated.
345   fields = [fields filteredArrayUsingPredicate:
346       [NSPredicate predicateWithBlock:
347           ^BOOL(NSControl<AutofillInputField>* field, NSDictionary* bindings) {
348               return [field isEnabled];
349           }]];
351   autofill::DetailOutputMap detailOutputs;
352   [self fillDetailOutputs:&detailOutputs fromControls:fields];
353   autofill::ValidityMessages messages = delegate_->InputsAreValid(
354       section_, detailOutputs);
356   for (NSControl<AutofillInputField>* input in fields) {
357     const autofill::ServerFieldType type = [self fieldTypeForControl:input];
358     const autofill::ValidityMessage& message =
359         messages.GetMessageOrDefault(type);
360     if (validationType != autofill::VALIDATE_FINAL && !message.sure)
361       continue;
362     [input setValidityMessage:base::SysUTF16ToNSString(message.text)];
363     [validationDelegate_ updateMessageForField:input];
364   }
366   return !messages.HasErrors();
369 #pragma mark Internal API for AutofillSectionContainer.
371 - (void)textfieldEditedOrActivated:(NSControl<AutofillInputField>*)field
372                             edited:(BOOL)edited {
373   AutofillTextField* textfield =
374       base::mac::ObjCCastStrict<AutofillTextField>(field);
376   // This only applies to textfields.
377   if (!textfield)
378     return;
380   autofill::ServerFieldType type = [self fieldTypeForControl:field];
381   string16 fieldValue = base::SysNSStringToUTF16([textfield fieldValue]);
383   // Get the frame rectangle for the designated field, in screen coordinates.
384   NSRect textFrameInScreen = [field convertRect:[field bounds] toView:nil];
385   textFrameInScreen.origin =
386       [[field window] convertBaseToScreen:textFrameInScreen.origin];
388   // And adjust for gfx::Rect being flipped compared to OSX coordinates.
389   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
390   textFrameInScreen.origin.y =
391       NSMaxY([screen frame]) - NSMaxY(textFrameInScreen);
392   gfx::Rect textFrameRect(NSRectToCGRect(textFrameInScreen));
394   delegate_->UserEditedOrActivatedInput(section_,
395                                           [self detailInputForType:type],
396                                           [self view],
397                                           textFrameRect,
398                                           fieldValue,
399                                           edited);
401   // If the field is marked as invalid, check if the text is now valid.
402   // Many fields (i.e. CC#) are invalid for most of the duration of editing,
403   // so flagging them as invalid prematurely is not helpful. However,
404   // correcting a minor mistake (i.e. a wrong CC digit) should immediately
405   // result in validation - positive user feedback.
406   if ([textfield invalid] && edited) {
407     string16 message = delegate_->InputValidityMessage(section_,
408                                                          type,
409                                                          fieldValue);
410     [textfield setValidityMessage:base::SysUTF16ToNSString(message)];
411     [validationDelegate_ updateMessageForField:textfield];
413     // If the field transitioned from invalid to valid, re-validate the group,
414     // since inter-field checks become meaningful with valid fields.
415     if (![textfield invalid])
416       [self validateFor:autofill::VALIDATE_EDIT];
417   }
419   // Update the icon for the textfield.
420   gfx::Image icon = delegate_->IconForField(type, fieldValue);
421   if (!icon.IsEmpty()) {
422     [[textfield cell] setIcon:icon.ToNSImage()];
423   }
426 - (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control {
427   DCHECK([control tag]);
428   return static_cast<autofill::ServerFieldType>([control tag]);
431 - (const autofill::DetailInput*)detailInputForType:
432     (autofill::ServerFieldType)type {
433   for (size_t i = 0; i < detailInputs_.size(); ++i) {
434     if (detailInputs_[i]->type == type)
435       return detailInputs_[i];
436   }
437   // TODO(groby): Needs to be NOTREACHED. Can't, due to the fact that tests
438   // blindly call setFieldValue:forInput:, even for non-existing inputs.
439   return NULL;
442 - (void)fillDetailOutputs:(autofill::DetailOutputMap*)outputs
443              fromControls:(NSArray*)controls {
444   for (NSControl<AutofillInputField>* input in controls) {
445     DCHECK([input isKindOfClass:[NSControl class]]);
446     DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]);
447     autofill::ServerFieldType fieldType = [self fieldTypeForControl:input];
448     DCHECK([self detailInputForType:fieldType]);
449     NSString* value = [input fieldValue];
450     outputs->insert(std::make_pair([self detailInputForType:fieldType],
451                                    base::SysNSStringToUTF16(value)));
452   }
455 - (NSTextField*)makeDetailSectionLabel:(NSString*)labelText {
456   base::scoped_nsobject<NSTextField> label([[NSTextField alloc] init]);
457   [label setFont:
458       [[NSFontManager sharedFontManager] convertFont:[label font]
459                                          toHaveTrait:NSBoldFontMask]];
460   [label setStringValue:labelText];
461   [label setEditable:NO];
462   [label setBordered:NO];
463   [label setDrawsBackground:NO];
464   [label sizeToFit];
465   return label.autorelease();
468 - (void)updateAndClobber:(BOOL)shouldClobber {
469   const autofill::DetailInputs& updatedInputs =
470       delegate_->RequestedFieldsForSection(section_);
472   for (autofill::DetailInputs::const_iterator iter = updatedInputs.begin();
473        iter != updatedInputs.end();
474        ++iter) {
475     NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type];
476     DCHECK(field);
478     [field setEnabled:iter->editable];
480     if (shouldClobber || [field isDefault]) {
481       [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)];
482       AutofillTextField* textField =
483           base::mac::ObjCCast<AutofillTextField>(field);
484       if (textField) {
485         gfx::Image icon =
486             delegate_->IconForField(iter->type, iter->initial_value);
487         if (!icon.IsEmpty()) {
488           [[textField cell] setIcon:icon.ToNSImage()];
489         }
490       }
491     }
492     if (shouldClobber)
493       [field setValidityMessage:@""];
494   }
495   [self modelChanged];
498 - (MenuButton*)makeSuggestionButton {
499   base::scoped_nsobject<MenuButton> button([[MenuButton alloc] init]);
501   [button setOpenMenuOnClick:YES];
502   [button setBordered:NO];
503   [button setShowsBorderOnlyWhileMouseInside:YES];
505   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
506   NSImage* image =
507       rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON).ToNSImage();
508   [[button cell] setImage:image
509            forButtonState:image_button_cell::kDefaultState];
510   image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_H).
511       ToNSImage();
512   [[button cell] setImage:image
513            forButtonState:image_button_cell::kHoverState];
514   image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_P).
515       ToNSImage();
516   [[button cell] setImage:image
517            forButtonState:image_button_cell::kPressedState];
518   image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_D).
519       ToNSImage();
520   [[button cell] setImage:image
521            forButtonState:image_button_cell::kDisabledState];
523   // ImageButtonCell's cellSize is not working. (http://crbug.com/298501)
524   [button setFrameSize:[image size]];
525   return button.autorelease();
528 // TODO(estade): we should be using Chrome-style constrained window padding
529 // values.
530 - (LayoutView*)makeInputControls {
531   base::scoped_nsobject<LayoutView> view([[LayoutView alloc] init]);
532   [view setLayoutManager:
533       scoped_ptr<SimpleGridLayout>(new SimpleGridLayout(view))];
534   SimpleGridLayout* layout = [view layoutManager];
536   // Reverse order of rows, but keep order of fields stable. stable_sort
537   // guarantees that field order within a row is not affected.
538   // Necessary since OSX builds forms from the bottom left.
539   std::stable_sort(
540       detailInputs_.begin(), detailInputs_.end(), CompareInputRows);
541   for (size_t i = 0; i < detailInputs_.size(); ++i) {
542     const autofill::DetailInput& input = *detailInputs_[i];
543     int kColumnSetId = input.row_id;
544     ColumnSet* columnSet = layout->GetColumnSet(kColumnSetId);
545     if (!columnSet) {
546       // Create a new column set and row.
547       columnSet = layout->AddColumnSet(kColumnSetId);
548       if (i != 0 && kColumnSetId != -1)
549         layout->AddPaddingRow(kRelatedControlVerticalSpacing);
550       layout->StartRow(0, kColumnSetId);
551     } else {
552       // Add a new column to existing row.
553       columnSet->AddPaddingColumn(kRelatedControlHorizontalSpacing);
554       // Must explicitly skip the padding column since we've already started
555       // adding views.
556       layout->SkipColumns(1);
557     }
559     columnSet->AddColumn(input.expand_weight ? input.expand_weight : 1.0f);
561     ui::ComboboxModel* inputModel =
562         delegate_->ComboboxModelForAutofillType(input.type);
563     base::scoped_nsprotocol<NSControl<AutofillInputField>*> control;
564     if (inputModel) {
565       base::scoped_nsobject<AutofillPopUpButton> popup(
566           [[AutofillPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]);
567       for (int i = 0; i < inputModel->GetItemCount(); ++i) {
568          [popup addItemWithTitle:
569              base::SysUTF16ToNSString(inputModel->GetItemAt(i))];
570       }
571       [popup setDefaultValue:base::SysUTF16ToNSString(
572           inputModel->GetItemAt(inputModel->GetDefaultIndex()))];
573       control.reset(popup.release());
574     } else {
575       base::scoped_nsobject<AutofillTextField> field(
576           [[AutofillTextField alloc] init]);
577       [[field cell] setPlaceholderString:
578           l10n_util::GetNSStringWithFixup(input.placeholder_text_rid)];
579       [[field cell] setIcon:
580           delegate_->IconForField(
581               input.type, input.initial_value).AsNSImage()];
582       [field setDefaultValue:@""];
583       control.reset(field.release());
584     }
585     [control setFieldValue:base::SysUTF16ToNSString(input.initial_value)];
586     [control sizeToFit];
587     [control setTag:input.type];
588     [control setDelegate:self];
589     // Hide away fields that cannot be edited.
590     if (kColumnSetId == -1) {
591       [control setFrame:NSZeroRect];
592       [control setHidden:YES];
593     }
594     layout->AddView(control);
595   }
597   return view.autorelease();
600 @end
603 @implementation AutofillSectionContainer (ForTesting)
605 - (NSControl*)getField:(autofill::ServerFieldType)type {
606   return [inputs_ viewWithTag:type];
609 - (void)setFieldValue:(NSString*)text
610              forInput:(const autofill::DetailInput&)input {
611   if ([self detailInputForType:input.type] != &input)
612     return;
614   NSControl<AutofillInputField>* field = [inputs_ viewWithTag:input.type];
615   [field setFieldValue:text];
618 - (void)setSuggestionFieldValue:(NSString*)text {
619   [[suggestContainer_ inputField] setFieldValue:text];
622 - (void)activateFieldForInput:(const autofill::DetailInput&)input {
623   if ([self detailInputForType:input.type] != &input)
624     return;
626   NSControl<AutofillInputField>* field = [inputs_ viewWithTag:input.type];
627   [[field window] makeFirstResponder:field];
630 @end