OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/autocomplete/autocomplete_edit.h" | 5 #include "chrome/browser/autocomplete/autocomplete_edit.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 814 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
825 call_controller_onchanged = false; | 825 call_controller_onchanged = false; |
826 } | 826 } |
827 | 827 |
828 // We need to invoke OnChanged in case the destination url changed (as could | 828 // We need to invoke OnChanged in case the destination url changed (as could |
829 // happen when control is toggled). | 829 // happen when control is toggled). |
830 if (call_controller_onchanged) | 830 if (call_controller_onchanged) |
831 OnChanged(); | 831 OnChanged(); |
832 } | 832 } |
833 | 833 |
834 bool AutocompleteEditModel::OnAfterPossibleChange( | 834 bool AutocompleteEditModel::OnAfterPossibleChange( |
| 835 const string16& old_text, |
835 const string16& new_text, | 836 const string16& new_text, |
836 size_t selection_start, | 837 size_t selection_start, |
837 size_t selection_end, | 838 size_t selection_end, |
838 bool selection_differs, | 839 bool selection_differs, |
839 bool text_differs, | 840 bool text_differs, |
840 bool just_deleted_text, | 841 bool just_deleted_text, |
841 bool allow_keyword_ui_change) { | 842 bool allow_keyword_ui_change) { |
842 // Update the paste state as appropriate: if we're just finishing a paste | 843 // Update the paste state as appropriate: if we're just finishing a paste |
843 // that replaced all the text, preserve that information; otherwise, if we've | 844 // that replaced all the text, preserve that information; otherwise, if we've |
844 // made some other edit, clear paste tracking. | 845 // made some other edit, clear paste tracking. |
(...skipping 11 matching lines...) Expand all Loading... |
856 // to update the popup if it's open, since the desired_tld will have changed. | 857 // to update the popup if it's open, since the desired_tld will have changed. |
857 if ((text_differs || selection_differs) && | 858 if ((text_differs || selection_differs) && |
858 (control_key_state_ == DOWN_WITHOUT_CHANGE)) { | 859 (control_key_state_ == DOWN_WITHOUT_CHANGE)) { |
859 control_key_state_ = DOWN_WITH_CHANGE; | 860 control_key_state_ = DOWN_WITH_CHANGE; |
860 if (!text_differs && !popup_->IsOpen()) | 861 if (!text_differs && !popup_->IsOpen()) |
861 return false; // Don't open the popup for no reason. | 862 return false; // Don't open the popup for no reason. |
862 } else if (!user_text_changed) { | 863 } else if (!user_text_changed) { |
863 return false; | 864 return false; |
864 } | 865 } |
865 | 866 |
866 const string16 old_user_text = user_text_; | |
867 // If the user text has not changed, we do not want to change the model's | 867 // If the user text has not changed, we do not want to change the model's |
868 // state associated with the text. Otherwise, we can get surprising behavior | 868 // state associated with the text. Otherwise, we can get surprising behavior |
869 // where the autocompleted text unexpectedly reappears, e.g. crbug.com/55983 | 869 // where the autocompleted text unexpectedly reappears, e.g. crbug.com/55983 |
870 if (user_text_changed) { | 870 if (user_text_changed) { |
871 InternalSetUserText(UserTextFromDisplayText(new_text)); | 871 InternalSetUserText(UserTextFromDisplayText(new_text)); |
872 has_temporary_text_ = false; | 872 has_temporary_text_ = false; |
873 | 873 |
874 // Track when the user has deleted text so we won't allow inline | 874 // Track when the user has deleted text so we won't allow inline |
875 // autocomplete. | 875 // autocomplete. |
876 just_deleted_text_ = just_deleted_text; | 876 just_deleted_text_ = just_deleted_text; |
877 } | 877 } |
878 | 878 |
879 const bool no_selection = selection_start == selection_end; | 879 const bool no_selection = selection_start == selection_end; |
880 | 880 |
881 // Update the popup for the change, in the process changing to keyword mode | 881 // Update the popup for the change, in the process changing to keyword mode |
882 // if the user hit space in mid-string after a keyword. | 882 // if the user hit space in mid-string after a keyword. |
883 // |allow_exact_keyword_match_| will be used by StartAutocomplete() method, | 883 // |allow_exact_keyword_match_| will be used by StartAutocomplete() method, |
884 // which will be called by |view_->UpdatePopup()|. So we can safely clear | 884 // which will be called by |view_->UpdatePopup()|; so after that returns we |
885 // this flag afterwards. | 885 // can safely reset this flag. |
886 allow_exact_keyword_match_ = | 886 allow_exact_keyword_match_ = text_differs && allow_keyword_ui_change && |
887 text_differs && allow_keyword_ui_change && | |
888 !just_deleted_text && no_selection && | 887 !just_deleted_text && no_selection && |
889 ShouldAllowExactKeywordMatch(old_user_text, user_text_, selection_start); | 888 CreatedKeywordSearchByInsertingSpaceInMiddle(old_text, user_text_, |
| 889 selection_start); |
890 view_->UpdatePopup(); | 890 view_->UpdatePopup(); |
891 allow_exact_keyword_match_ = false; | 891 allow_exact_keyword_match_ = false; |
892 | 892 |
893 // Change to keyword mode if the user has typed a keyword name and is now | 893 // Change to keyword mode if the user is now pressing space after a keyword |
894 // pressing space after the name. Accepting the keyword will update our | 894 // name. Note that if this is the case, then even if there was no keyword |
895 // state, so in that case there's no need to also return true here. | 895 // hint when we entered this function (e.g. if the user has used space to |
| 896 // replace some selected text that was adjoined to this keyword), there will |
| 897 // be one now because of the call to UpdatePopup() above; so it's safe for |
| 898 // MaybeAcceptKeywordBySpace() to look at |keyword_| and |is_keyword_hint_| to |
| 899 // determine what keyword, if any, is applicable. |
| 900 // |
| 901 // If MaybeAcceptKeywordBySpace() accepts the keyword and returns true, that |
| 902 // will have updated our state already, so in that case we don't also return |
| 903 // true from this function. |
896 return !(text_differs && allow_keyword_ui_change && !just_deleted_text && | 904 return !(text_differs && allow_keyword_ui_change && !just_deleted_text && |
897 no_selection && selection_start == user_text_.length() && | 905 no_selection && (selection_start == user_text_.length()) && |
898 MaybeAcceptKeywordBySpace(old_user_text, user_text_)); | 906 MaybeAcceptKeywordBySpace(user_text_)); |
899 } | 907 } |
900 | 908 |
901 void AutocompleteEditModel::PopupBoundsChangedTo(const gfx::Rect& bounds) { | 909 void AutocompleteEditModel::PopupBoundsChangedTo(const gfx::Rect& bounds) { |
902 InstantController* instant = controller_->GetInstant(); | 910 InstantController* instant = controller_->GetInstant(); |
903 if (instant) | 911 if (instant) |
904 instant->SetOmniboxBounds(bounds); | 912 instant->SetOmniboxBounds(bounds); |
905 } | 913 } |
906 | 914 |
907 void AutocompleteEditModel::OnResultChanged(bool default_match_changed) { | 915 void AutocompleteEditModel::OnResultChanged(bool default_match_changed) { |
908 const bool was_open = popup_->IsOpen(); | 916 const bool was_open = popup_->IsOpen(); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1023 // text they typed and change back to the default item. | 1031 // text they typed and change back to the default item. |
1024 // NOTE: This purposefully does not reset paste_state_. | 1032 // NOTE: This purposefully does not reset paste_state_. |
1025 just_deleted_text_ = false; | 1033 just_deleted_text_ = false; |
1026 has_temporary_text_ = false; | 1034 has_temporary_text_ = false; |
1027 if (revert_popup) | 1035 if (revert_popup) |
1028 popup_->ResetToDefaultMatch(); | 1036 popup_->ResetToDefaultMatch(); |
1029 view_->OnRevertTemporaryText(); | 1037 view_->OnRevertTemporaryText(); |
1030 } | 1038 } |
1031 | 1039 |
1032 bool AutocompleteEditModel::MaybeAcceptKeywordBySpace( | 1040 bool AutocompleteEditModel::MaybeAcceptKeywordBySpace( |
1033 const string16& old_user_text, | 1041 const string16& new_text) { |
1034 const string16& new_user_text) { | 1042 size_t keyword_length = new_text.length() - 1; |
1035 return (paste_state_ == NONE) && is_keyword_hint_ && !keyword_.empty() && | 1043 return (paste_state_ == NONE) && is_keyword_hint_ && !keyword_.empty() && |
1036 inline_autocomplete_text_.empty() && new_user_text.length() >= 2 && | 1044 inline_autocomplete_text_.empty() && |
1037 IsSpaceCharForAcceptingKeyword(*new_user_text.rbegin()) && | 1045 (keyword_.length() == keyword_length) && |
1038 !IsWhitespace(*(new_user_text.rbegin() + 1)) && | 1046 IsSpaceCharForAcceptingKeyword(new_text[keyword_length]) && |
1039 (old_user_text.length() + 1 >= new_user_text.length()) && | 1047 !new_text.compare(0, keyword_length, keyword_, 0, keyword_length) && |
1040 !new_user_text.compare(0, new_user_text.length() - 1, old_user_text, | |
1041 0, new_user_text.length() - 1) && | |
1042 AcceptKeyword(); | 1048 AcceptKeyword(); |
1043 } | 1049 } |
1044 | 1050 |
1045 bool AutocompleteEditModel::ShouldAllowExactKeywordMatch( | 1051 bool AutocompleteEditModel::CreatedKeywordSearchByInsertingSpaceInMiddle( |
1046 const string16& old_user_text, | 1052 const string16& old_text, |
1047 const string16& new_user_text, | 1053 const string16& new_text, |
1048 size_t caret_position) { | 1054 size_t caret_position) const { |
| 1055 DCHECK_GE(new_text.length(), caret_position); |
| 1056 |
1049 // Check simple conditions first. | 1057 // Check simple conditions first. |
1050 if (paste_state_ != NONE || caret_position < 2 || | 1058 if ((paste_state_ != NONE) || (caret_position < 2) || |
1051 new_user_text.length() <= caret_position || | 1059 (old_text.length() < caret_position) || |
1052 old_user_text.length() < caret_position || | 1060 (new_text.length() == caret_position)) |
1053 !IsSpaceCharForAcceptingKeyword(new_user_text[caret_position - 1]) || | 1061 return false; |
1054 IsSpaceCharForAcceptingKeyword(new_user_text[caret_position - 2]) || | 1062 size_t space_position = caret_position - 1; |
1055 new_user_text.compare(0, caret_position - 1, old_user_text, | 1063 if (!IsSpaceCharForAcceptingKeyword(new_text[space_position]) || |
1056 0, caret_position - 1) || | 1064 IsWhitespace(new_text[space_position - 1]) || |
1057 !new_user_text.compare(caret_position - 1, | 1065 new_text.compare(0, space_position, old_text, 0, space_position) || |
1058 new_user_text.length() - caret_position + 1, | 1066 !new_text.compare(space_position, new_text.length() - space_position, |
1059 old_user_text, caret_position - 1, | 1067 old_text, space_position, |
1060 old_user_text.length() - caret_position + 1)) { | 1068 old_text.length() - space_position)) { |
1061 return false; | 1069 return false; |
1062 } | 1070 } |
1063 | 1071 |
1064 // Then check if the text before the inserted space matches a keyword. | 1072 // Then check if the text before the inserted space matches a keyword. |
1065 string16 keyword; | 1073 string16 keyword; |
1066 TrimWhitespace(new_user_text.substr(0, caret_position - 1), | 1074 TrimWhitespace(new_text.substr(0, space_position), TRIM_LEADING, &keyword); |
1067 TRIM_LEADING, &keyword); | 1075 return !keyword.empty() && |
1068 | |
1069 // Only allow exact keyword match if |keyword| represents a keyword hint. | |
1070 return keyword.length() && | |
1071 !autocomplete_controller_->keyword_provider()-> | 1076 !autocomplete_controller_->keyword_provider()-> |
1072 GetKeywordForText(keyword).empty(); | 1077 GetKeywordForText(keyword).empty(); |
1073 } | 1078 } |
1074 | 1079 |
1075 bool AutocompleteEditModel::DoInstant(const AutocompleteMatch& match, | 1080 bool AutocompleteEditModel::DoInstant(const AutocompleteMatch& match, |
1076 string16* suggested_text) { | 1081 string16* suggested_text) { |
1077 DCHECK(suggested_text); | 1082 DCHECK(suggested_text); |
1078 | 1083 |
1079 if (in_revert_) | 1084 if (in_revert_) |
1080 return false; | 1085 return false; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1138 // static | 1143 // static |
1139 bool AutocompleteEditModel::IsSpaceCharForAcceptingKeyword(wchar_t c) { | 1144 bool AutocompleteEditModel::IsSpaceCharForAcceptingKeyword(wchar_t c) { |
1140 switch (c) { | 1145 switch (c) { |
1141 case 0x0020: // Space | 1146 case 0x0020: // Space |
1142 case 0x3000: // Ideographic Space | 1147 case 0x3000: // Ideographic Space |
1143 return true; | 1148 return true; |
1144 default: | 1149 default: |
1145 return false; | 1150 return false; |
1146 } | 1151 } |
1147 } | 1152 } |
OLD | NEW |