| Index: chrome/browser/autocomplete/autocomplete_edit.cc | 
| diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc | 
| index c4d782a07b4c081d1d2baa35e4209f24db282a64..1ad993eeb638778287a9157989000b414948c009 100644 | 
| --- a/chrome/browser/autocomplete/autocomplete_edit.cc | 
| +++ b/chrome/browser/autocomplete/autocomplete_edit.cc | 
| @@ -45,13 +45,11 @@ AutocompleteEditController::~AutocompleteEditController() { | 
| AutocompleteEditModel::State::State(bool user_input_in_progress, | 
| const std::wstring& user_text, | 
| const std::wstring& keyword, | 
| -                                    bool is_keyword_hint, | 
| -                                    KeywordUIState keyword_ui_state) | 
| +                                    bool is_keyword_hint) | 
| : user_input_in_progress(user_input_in_progress), | 
| user_text(user_text), | 
| keyword(keyword), | 
| -      is_keyword_hint(is_keyword_hint), | 
| -      keyword_ui_state(keyword_ui_state) { | 
| +      is_keyword_hint(is_keyword_hint) { | 
| } | 
|  | 
| AutocompleteEditModel::State::~State() { | 
| @@ -71,11 +69,9 @@ AutocompleteEditModel::AutocompleteEditModel( | 
| user_input_in_progress_(false), | 
| just_deleted_text_(false), | 
| has_temporary_text_(false), | 
| -      original_keyword_ui_state_(NORMAL), | 
| paste_state_(NONE), | 
| control_key_state_(UP), | 
| is_keyword_hint_(false), | 
| -      keyword_ui_state_(NORMAL), | 
| paste_and_go_transition_(PageTransition::TYPED), | 
| profile_(profile) { | 
| } | 
| @@ -114,8 +110,7 @@ const AutocompleteEditModel::State | 
| } | 
| } | 
|  | 
| -  return State(user_input_in_progress_, user_text_, keyword_, is_keyword_hint_, | 
| -               keyword_ui_state_); | 
| +  return State(user_input_in_progress_, user_text_, keyword_, is_keyword_hint_); | 
| } | 
|  | 
| void AutocompleteEditModel::RestoreState(const State& state) { | 
| @@ -125,7 +120,6 @@ void AutocompleteEditModel::RestoreState(const State& state) { | 
| // DisplayTextFromUserText(), as its result depends upon this state. | 
| keyword_ = state.keyword; | 
| is_keyword_hint_ = state.is_keyword_hint; | 
| -    keyword_ui_state_ = state.keyword_ui_state; | 
| view_->SetUserText(state.user_text, | 
| DisplayTextFromUserText(state.user_text), false); | 
| } | 
| @@ -296,7 +290,6 @@ void AutocompleteEditModel::Revert() { | 
| InternalSetUserText(std::wstring()); | 
| keyword_.clear(); | 
| is_keyword_hint_ = false; | 
| -  keyword_ui_state_ = NORMAL; | 
| has_temporary_text_ = false; | 
| view_->SetWindowTextAndCaretPos(permanent_text_, | 
| has_focus_ ? permanent_text_.length() : 0); | 
| @@ -305,11 +298,11 @@ void AutocompleteEditModel::Revert() { | 
| void AutocompleteEditModel::StartAutocomplete( | 
| bool has_selected_text, | 
| bool prevent_inline_autocomplete) const { | 
| +  bool keyword_is_selected = KeywordIsSelected(); | 
| popup_->StartAutocomplete(user_text_, GetDesiredTLD(), | 
| prevent_inline_autocomplete || just_deleted_text_ || | 
| (has_selected_text && inline_autocomplete_text_.empty()) || | 
| -      (paste_state_ != NONE), keyword_ui_state_ == KEYWORD, | 
| -      keyword_ui_state_ != NO_KEYWORD); | 
| +      (paste_state_ != NONE), keyword_is_selected, keyword_is_selected); | 
| } | 
|  | 
| bool AutocompleteEditModel::CanPasteAndGo(const std::wstring& text) const { | 
| @@ -438,17 +431,19 @@ void AutocompleteEditModel::OpenURL(const GURL& url, | 
| alternate_nav_url); | 
| } | 
|  | 
| -void AutocompleteEditModel::AcceptKeyword() { | 
| +bool AutocompleteEditModel::AcceptKeyword() { | 
| +  DCHECK(is_keyword_hint_ && !keyword_.empty()); | 
| + | 
| view_->OnBeforePossibleChange(); | 
| view_->SetWindowTextAndCaretPos(std::wstring(), 0); | 
| is_keyword_hint_ = false; | 
| -  keyword_ui_state_ = KEYWORD; | 
| view_->OnAfterPossibleChange(); | 
| just_deleted_text_ = false;  // OnAfterPossibleChange() erroneously sets this | 
| // since the edit contents have disappeared.  It | 
| // doesn't really matter, but we clear it to be | 
| // consistent. | 
| UserMetrics::RecordAction(UserMetricsAction("AcceptedKeywordHint"), profile_); | 
| +  return true; | 
| } | 
|  | 
| void AutocompleteEditModel::ClearKeyword(const std::wstring& visible_text) { | 
| @@ -456,7 +451,7 @@ void AutocompleteEditModel::ClearKeyword(const std::wstring& visible_text) { | 
| const std::wstring window_text(keyword_ + visible_text); | 
| view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length()); | 
| keyword_.clear(); | 
| -  keyword_ui_state_ = NORMAL; | 
| +  is_keyword_hint_ = false; | 
| view_->OnAfterPossibleChange(); | 
| just_deleted_text_ = true;  // OnAfterPossibleChange() fails to clear this | 
| // since the edit contents have actually grown | 
| @@ -502,7 +497,6 @@ bool AutocompleteEditModel::OnEscapeKeyPressed() { | 
| // NOTE: This purposefully does not reset paste_state_. | 
| just_deleted_text_ = false; | 
| has_temporary_text_ = false; | 
| -      keyword_ui_state_ = original_keyword_ui_state_; | 
| popup_->ResetToDefaultMatch(); | 
| view_->OnRevertTemporaryText(); | 
| return true; | 
| @@ -576,21 +570,15 @@ void AutocompleteEditModel::OnPopupDataChanged( | 
| GURL* destination_for_temporary_text_change, | 
| const std::wstring& keyword, | 
| bool is_keyword_hint) { | 
| -  KeywordUIState old_keyword_ui_state = keyword_ui_state_; | 
| - | 
| // Update keyword/hint-related local state. | 
| bool keyword_state_changed = (keyword_ != keyword) || | 
| ((is_keyword_hint_ != is_keyword_hint) && !keyword.empty()); | 
| if (keyword_state_changed) { | 
| keyword_ = keyword; | 
| is_keyword_hint_ = is_keyword_hint; | 
| -  } | 
|  | 
| -  // Update |keyword_ui_state_| only when necessary. It may be changed even if | 
| -  // |keyword_state_changed| is false. | 
| -  if (keyword_ui_state_ != NO_KEYWORD) { | 
| -    keyword_ui_state_ = (is_keyword_hint_ || keyword_.empty()) ? | 
| -        NORMAL : KEYWORD; | 
| +    // |is_keyword_hint_| should always be false if |keyword_| is empty. | 
| +    DCHECK(!keyword_.empty() || !is_keyword_hint_); | 
| } | 
|  | 
| // Handle changes to temporary text. | 
| @@ -600,7 +588,6 @@ void AutocompleteEditModel::OnPopupDataChanged( | 
| // Save the original selection and URL so it can be reverted later. | 
| has_temporary_text_ = true; | 
| original_url_ = *destination_for_temporary_text_change; | 
| -      original_keyword_ui_state_ = old_keyword_ui_state; | 
| inline_autocomplete_text_.clear(); | 
| } | 
| if (control_key_state_ == DOWN_WITHOUT_CHANGE) { | 
| @@ -643,8 +630,8 @@ bool AutocompleteEditModel::OnAfterPossibleChange( | 
| // Update the paste state as appropriate: if we're just finishing a paste | 
| // that replaced all the text, preserve that information; otherwise, if we've | 
| // made some other edit, clear paste tracking. | 
| -  if (paste_state_ == REPLACING_ALL) | 
| -    paste_state_ = REPLACED_ALL; | 
| +  if (paste_state_ == PASTING) | 
| +    paste_state_ = PASTED; | 
| else if (text_differs) | 
| paste_state_ = NONE; | 
|  | 
| @@ -664,13 +651,20 @@ bool AutocompleteEditModel::OnAfterPossibleChange( | 
| return false; | 
| } | 
|  | 
| -  const bool had_keyword = KeywordIsSelected(); | 
| - | 
| // If the user text has not changed, we do not want to change the model's | 
| // state associated with the text.  Otherwise, we can get surprising behavior | 
| // where the autocompleted text unexpectedly reappears, e.g. crbug.com/55983 | 
| if (user_text_changed) { | 
| -    InternalSetUserText(UserTextFromDisplayText(new_text)); | 
| +    const std::wstring new_user_text = UserTextFromDisplayText(new_text); | 
| + | 
| +    // Try to accept the current keyword if the user only typed a space at the | 
| +    // end of content. Model's state and popup will be updated when the keyword | 
| +    // is accepted. So we just need to return false here. | 
| +    if (allow_keyword_ui_change && !selection_differs && | 
| +        MaybeAcceptKeywordBySpace(new_user_text)) | 
| +      return false; | 
| + | 
| +    InternalSetUserText(new_user_text); | 
| has_temporary_text_ = false; | 
|  | 
| // Track when the user has deleted text so we won't allow inline | 
| @@ -678,17 +672,6 @@ bool AutocompleteEditModel::OnAfterPossibleChange( | 
| just_deleted_text_ = just_deleted_text; | 
| } | 
|  | 
| -  // Disable the fancy keyword UI if the user didn't already have a visible | 
| -  // keyword and the view doesn't want us to change the keyword UI state. | 
| -  // This prevents us from showing the fancy UI and interrupting the user's | 
| -  // editing if, for example, the user happens to have a keyword for 'a', | 
| -  // types 'ab' then puts a space between the 'a' and the 'b'. | 
| -  // If |keyword_ui_state_| is set to NORMAL here, then it will be updated | 
| -  // to a proper value in OnPopupDataChanged() method according to the new | 
| -  // match result. | 
| -  if (!had_keyword) | 
| -    keyword_ui_state_ = allow_keyword_ui_change ? NORMAL : NO_KEYWORD; | 
| - | 
| view_->UpdatePopup(); | 
| return true; | 
| } | 
| @@ -758,7 +741,7 @@ void AutocompleteEditModel::InternalSetUserText(const std::wstring& text) { | 
| } | 
|  | 
| bool AutocompleteEditModel::KeywordIsSelected() const { | 
| -  return keyword_ui_state_ == KEYWORD; | 
| +  return !is_keyword_hint_ && !keyword_.empty(); | 
| } | 
|  | 
| std::wstring AutocompleteEditModel::DisplayTextFromUserText( | 
| @@ -795,3 +778,25 @@ bool AutocompleteEditModel::GetURLForText(const std::wstring& text, | 
| *url = parsed_url; | 
| return true; | 
| } | 
| + | 
| +bool AutocompleteEditModel::MaybeAcceptKeywordBySpace( | 
| +    const std::wstring& new_user_text) { | 
| +  return (paste_state_ == NONE) && is_keyword_hint_ && !keyword_.empty() && | 
| +      inline_autocomplete_text_.empty() && !user_text_.empty() && | 
| +      (new_user_text.length() == user_text_.length() + 1) && | 
| +      !new_user_text.compare(0, user_text_.length(), user_text_) && | 
| +      IsSpaceCharForAcceptingKeyword(new_user_text[user_text_.length()]) && | 
| +      !IsWhitespace(user_text_[user_text_.length() - 1]) && | 
| +      AcceptKeyword(); | 
| +} | 
| + | 
| +//  static | 
| +bool AutocompleteEditModel::IsSpaceCharForAcceptingKeyword(wchar_t c) { | 
| +  switch (c) { | 
| +    case 0x0020:  // Space | 
| +    case 0x3000:  // Ideographic Space | 
| +      return true; | 
| +    default: | 
| +      return false; | 
| +  } | 
| +} | 
|  |