Index: chrome/browser/ui/cocoa/autofill/autofill_section_container.mm |
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm |
index d67a1b3b5fca636ad9ba70ce9bae75476568bdc7..e45bb81046f70ffb92b3593a644cb8d1be8eb8a3 100644 |
--- a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm |
+++ b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm |
@@ -72,10 +72,10 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
@interface AutofillSectionContainer () |
-// A text field has been edited or activated - inform the delegate that it's |
-// time to show a suggestion popup & possibly reset the validity of the input. |
-- (void)textfieldEditedOrActivated:(NSControl<AutofillInputField>*)field |
- edited:(BOOL)edited; |
+// An input field has been edited or activated - inform the delegate and |
+// possibly reset the validity of the input (if it's a textfield). |
+- (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field |
+ edited:(BOOL)edited; |
// Convenience method to retrieve a field type via the control's tag. |
- (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control; |
@@ -104,8 +104,8 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
// Create a button offering input suggestions. |
- (MenuButton*)makeSuggestionButton; |
-// Create a view with all inputs requested by |delegate_|. Autoreleased. |
-- (LayoutView*)makeInputControls; |
+// Create a view with all inputs requested by |delegate_| and resets |input_|. |
+- (void)makeInputControls; |
// Refresh all field icons based on |delegate_| status. |
- (void)updateFieldIcons; |
@@ -159,27 +159,8 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
- (void)loadView { |
- // Keep a list of weak pointers to DetailInputs. |
- const autofill::DetailInputs& inputs = |
- delegate_->RequestedFieldsForSection(section_); |
- |
- // Reverse the order of all the inputs. |
- for (int i = inputs.size() - 1; i >= 0; --i) { |
- detailInputs_.push_back(&(inputs[i])); |
- } |
+ [self makeInputControls]; |
- // Then right the reversal in each row. |
- std::vector<const autofill::DetailInput*>::iterator it; |
- for (it = detailInputs_.begin(); it < detailInputs_.end(); ++it) { |
- std::vector<const autofill::DetailInput*>::iterator start = it; |
- while (it != detailInputs_.end() && |
- (*it)->length != autofill::DetailInput::LONG) { |
- ++it; |
- } |
- std::reverse(start, it); |
- } |
- |
- inputs_.reset([[self makeInputControls] retain]); |
base::string16 labelText = delegate_->LabelForSection(section_); |
label_.reset( |
[[self makeDetailSectionLabel:base::SysUTF16ToNSString(labelText)] |
@@ -288,7 +269,7 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
- (void)onMouseDown:(NSControl<AutofillInputField>*)field { |
- [self textfieldEditedOrActivated:field edited:NO]; |
+ [self fieldEditedOrActivated:field edited:NO]; |
[validationDelegate_ updateMessageForField:field]; |
} |
@@ -297,7 +278,7 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
- (void)didChange:(id)sender { |
- [self textfieldEditedOrActivated:sender edited:YES]; |
+ [self fieldEditedOrActivated:sender edited:YES]; |
} |
- (void)didEndEditing:(id)sender { |
@@ -419,17 +400,10 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
#pragma mark Internal API for AutofillSectionContainer. |
-- (void)textfieldEditedOrActivated:(NSControl<AutofillInputField>*)field |
- edited:(BOOL)edited { |
- AutofillTextField* textfield = |
- base::mac::ObjCCastStrict<AutofillTextField>(field); |
- |
- // This only applies to textfields. |
- if (!textfield) |
- return; |
- |
+- (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field |
+ edited:(BOOL)edited { |
autofill::ServerFieldType type = [self fieldTypeForControl:field]; |
- base::string16 fieldValue = base::SysNSStringToUTF16([textfield fieldValue]); |
+ base::string16 fieldValue = base::SysNSStringToUTF16([field fieldValue]); |
// Get the frame rectangle for the designated field, in screen coordinates. |
NSRect textFrameInScreen = [field convertRect:[field bounds] toView:nil]; |
@@ -449,15 +423,19 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
fieldValue, |
edited); |
- // If the field is marked as invalid, check if the text is now valid. |
- // Many fields (i.e. CC#) are invalid for most of the duration of editing, |
- // so flagging them as invalid prematurely is not helpful. However, |
- // correcting a minor mistake (i.e. a wrong CC digit) should immediately |
- // result in validation - positive user feedback. |
+ AutofillTextField* textfield = base::mac::ObjCCast<AutofillTextField>(field); |
+ if (!textfield) |
+ return; |
+ |
+ // If the field is marked as invalid, check if the text is now valid. Many |
+ // fields (i.e. CC#) are invalid for most of the duration of editing, so |
+ // flagging them as invalid prematurely is not helpful. However, correcting a |
+ // minor mistake (i.e. a wrong CC digit) should immediately result in |
+ // validation - positive user feedback. |
if ([textfield invalid] && edited) { |
base::string16 message = delegate_->InputValidityMessage(section_, |
- type, |
- fieldValue); |
+ type, |
+ fieldValue); |
[textfield setValidityMessage:base::SysUTF16ToNSString(message)]; |
[validationDelegate_ updateMessageForField:textfield]; |
@@ -485,7 +463,7 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
return detailInputs_[i]; |
} |
// TODO(groby): Needs to be NOTREACHED. Can't, due to the fact that tests |
- // blindly call setFieldValue:forInput:, even for non-existing inputs. |
+ // blindly call setFieldValue:forType:, even for non-existing inputs. |
return NULL; |
} |
@@ -514,23 +492,24 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
- (void)updateAndClobber:(BOOL)shouldClobber { |
- const autofill::DetailInputs& updatedInputs = |
- delegate_->RequestedFieldsForSection(section_); |
- |
- for (autofill::DetailInputs::const_iterator iter = updatedInputs.begin(); |
- iter != updatedInputs.end(); |
- ++iter) { |
- NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type]; |
- DCHECK(field); |
- |
- if (shouldClobber || [field isDefault]) { |
- [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)]; |
+ if (shouldClobber) { |
+ [self makeInputControls]; |
+ } else { |
+ const autofill::DetailInputs& updatedInputs = |
+ delegate_->RequestedFieldsForSection(section_); |
+ |
+ for (autofill::DetailInputs::const_iterator iter = updatedInputs.begin(); |
+ iter != updatedInputs.end(); |
+ ++iter) { |
+ NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type]; |
+ DCHECK(field); |
+ if ([field isDefault]) |
+ [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)]; |
} |
- if (shouldClobber) |
- [field setValidityMessage:@""]; |
+ [self updateFieldIcons]; |
} |
+ |
[self updateEditability]; |
- [self updateFieldIcons]; |
[self modelChanged]; |
} |
@@ -571,7 +550,38 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
// TODO(estade): we should be using Chrome-style constrained window padding |
// values. |
-- (LayoutView*)makeInputControls { |
+- (void)makeInputControls { |
+ if (inputs_) { |
+ // When |inputs_| is replaced in response to a country change, there's a |
+ // didEndEditing dispatched that segfaults or DCHECKS() as it's operating on |
+ // stale input fields. Nil out the input delegate so this doesn't happen. |
+ for (NSControl<AutofillInputField>* input in [inputs_ subviews]) { |
+ [input setInputDelegate:nil]; |
+ } |
+ } |
+ |
+ detailInputs_.clear(); |
+ |
+ // Keep a list of weak pointers to DetailInputs. |
+ const autofill::DetailInputs& inputs = |
+ delegate_->RequestedFieldsForSection(section_); |
+ |
+ // Reverse the order of all the inputs. |
+ for (int i = inputs.size() - 1; i >= 0; --i) { |
+ detailInputs_.push_back(&(inputs[i])); |
+ } |
+ |
+ // Then right the reversal in each row. |
+ std::vector<const autofill::DetailInput*>::iterator it; |
+ for (it = detailInputs_.begin(); it < detailInputs_.end(); ++it) { |
+ std::vector<const autofill::DetailInput*>::iterator start = it; |
+ while (it != detailInputs_.end() && |
+ (*it)->length != autofill::DetailInput::LONG) { |
+ ++it; |
+ } |
+ std::reverse(start, it); |
+ } |
+ |
base::scoped_nsobject<LayoutView> view([[LayoutView alloc] init]); |
[view setLayoutManager: |
scoped_ptr<SimpleGridLayout>(new SimpleGridLayout(view))]; |
@@ -625,11 +635,11 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
NSString* tooltipText = |
base::SysUTF16ToNSString(delegate_->TooltipForField(input.type)); |
if ([tooltipText length] > 0) { |
- DCHECK(!tooltipController_); |
- DCHECK(!tooltipField_); |
- tooltipController_.reset( |
- [[AutofillTooltipController alloc] |
- initWithArrowLocation:info_bubble::kTopRight]); |
+ if (!tooltipController_) { |
+ tooltipController_.reset( |
+ [[AutofillTooltipController alloc] |
+ initWithArrowLocation:info_bubble::kTopRight]); |
+ } |
tooltipField_ = field.get(); |
NSImage* icon = |
ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( |
@@ -658,8 +668,15 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
} |
+ if (inputs_) { |
+ [[self view] replaceSubview:inputs_ with:view]; |
+ id delegate = [[view_ window] windowController]; |
+ if ([delegate respondsToSelector:@selector(requestRelayout)]) |
+ [delegate performSelector:@selector(requestRelayout)]; |
+ } |
+ |
+ inputs_ = view; |
[self updateFieldIcons]; |
- return view.autorelease(); |
} |
- (void)updateFieldIcons { |
@@ -682,7 +699,6 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
} |
- (void)updateEditability { |
- |
base::scoped_nsobject<NSMutableArray> controls([[NSMutableArray alloc] init]); |
[self addInputsToArray:controls]; |
for (NSControl<AutofillInputField>* control in controls.get()) { |
@@ -716,7 +732,7 @@ bool ShouldOverwriteComboboxes(autofill::DialogSection section, |
NSControl<AutofillInputField>* field = [inputs_ viewWithTag:type]; |
if (field) { |
[[field window] makeFirstResponder:field]; |
- [self textfieldEditedOrActivated:field edited:NO]; |
+ [self fieldEditedOrActivated:field edited:NO]; |
} |
} |