| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 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 "components/omnibox/browser/omnibox_edit_model.h" | 5 #include "components/omnibox/browser/omnibox_edit_model.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 const char kOmniboxUserTextClearedHistogram[] = "Omnibox.UserTextCleared"; | 64 const char kOmniboxUserTextClearedHistogram[] = "Omnibox.UserTextCleared"; |
| 65 | 65 |
| 66 enum UserTextClearedType { | 66 enum UserTextClearedType { |
| 67 OMNIBOX_USER_TEXT_CLEARED_BY_EDITING = 0, | 67 OMNIBOX_USER_TEXT_CLEARED_BY_EDITING = 0, |
| 68 OMNIBOX_USER_TEXT_CLEARED_WITH_ESCAPE = 1, | 68 OMNIBOX_USER_TEXT_CLEARED_WITH_ESCAPE = 1, |
| 69 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS, | 69 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS, |
| 70 }; | 70 }; |
| 71 | 71 |
| 72 // Histogram name which counts the number of times the user enters | 72 // Histogram name which counts the number of times the user enters |
| 73 // keyword hint mode and via what method. The possible values are listed | 73 // keyword hint mode and via what method. The possible values are listed |
| 74 // in the EnteredKeywordModeMethod enum which is defined in the .h file. | 74 // in the KeywordModeEntryMethod enum which is defined in the .h file. |
| 75 const char kEnteredKeywordModeHistogram[] = "Omnibox.EnteredKeywordMode"; | 75 const char kEnteredKeywordModeHistogram[] = "Omnibox.EnteredKeywordMode"; |
| 76 | 76 |
| 77 // Histogram name which counts the number of milliseconds a user takes | 77 // Histogram name which counts the number of milliseconds a user takes |
| 78 // between focusing and editing the omnibox. | 78 // between focusing and editing the omnibox. |
| 79 const char kFocusToEditTimeHistogram[] = "Omnibox.FocusToEditTime"; | 79 const char kFocusToEditTimeHistogram[] = "Omnibox.FocusToEditTime"; |
| 80 | 80 |
| 81 // Histogram name which counts the number of milliseconds a user takes | 81 // Histogram name which counts the number of milliseconds a user takes |
| 82 // between focusing and opening an omnibox match. | 82 // between focusing and opening an omnibox match. |
| 83 const char kFocusToOpenTimeHistogram[] = "Omnibox.FocusToOpenTimeAnyPopupState"; | 83 const char kFocusToOpenTimeHistogram[] = "Omnibox.FocusToOpenTimeAnyPopupState"; |
| 84 | 84 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 } // namespace | 142 } // namespace |
| 143 | 143 |
| 144 | 144 |
| 145 // OmniboxEditModel::State ---------------------------------------------------- | 145 // OmniboxEditModel::State ---------------------------------------------------- |
| 146 | 146 |
| 147 OmniboxEditModel::State::State(bool user_input_in_progress, | 147 OmniboxEditModel::State::State(bool user_input_in_progress, |
| 148 const base::string16& user_text, | 148 const base::string16& user_text, |
| 149 const base::string16& gray_text, | 149 const base::string16& gray_text, |
| 150 const base::string16& keyword, | 150 const base::string16& keyword, |
| 151 bool is_keyword_hint, | 151 bool is_keyword_hint, |
| 152 KeywordModeEntryMethod keyword_mode_entry_method, |
| 152 bool url_replacement_enabled, | 153 bool url_replacement_enabled, |
| 153 OmniboxFocusState focus_state, | 154 OmniboxFocusState focus_state, |
| 154 FocusSource focus_source, | 155 FocusSource focus_source, |
| 155 const AutocompleteInput& autocomplete_input) | 156 const AutocompleteInput& autocomplete_input) |
| 156 : user_input_in_progress(user_input_in_progress), | 157 : user_input_in_progress(user_input_in_progress), |
| 157 user_text(user_text), | 158 user_text(user_text), |
| 158 gray_text(gray_text), | 159 gray_text(gray_text), |
| 159 keyword(keyword), | 160 keyword(keyword), |
| 160 is_keyword_hint(is_keyword_hint), | 161 is_keyword_hint(is_keyword_hint), |
| 162 keyword_mode_entry_method(keyword_mode_entry_method), |
| 161 url_replacement_enabled(url_replacement_enabled), | 163 url_replacement_enabled(url_replacement_enabled), |
| 162 focus_state(focus_state), | 164 focus_state(focus_state), |
| 163 focus_source(focus_source), | 165 focus_source(focus_source), |
| 164 autocomplete_input(autocomplete_input) { | 166 autocomplete_input(autocomplete_input) { |
| 165 } | 167 } |
| 166 | 168 |
| 167 OmniboxEditModel::State::State(const State& other) = default; | 169 OmniboxEditModel::State::State(const State& other) = default; |
| 168 | 170 |
| 169 OmniboxEditModel::State::~State() { | 171 OmniboxEditModel::State::~State() { |
| 170 } | 172 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 view_->SelectAll(true); | 212 view_->SelectAll(true); |
| 211 } else { | 213 } else { |
| 212 InternalSetUserText(display_text); | 214 InternalSetUserText(display_text); |
| 213 } | 215 } |
| 214 } | 216 } |
| 215 | 217 |
| 216 UMA_HISTOGRAM_BOOLEAN("Omnibox.SaveStateForTabSwitch.UserInputInProgress", | 218 UMA_HISTOGRAM_BOOLEAN("Omnibox.SaveStateForTabSwitch.UserInputInProgress", |
| 217 user_input_in_progress_); | 219 user_input_in_progress_); |
| 218 return State( | 220 return State( |
| 219 user_input_in_progress_, user_text_, view_->GetGrayTextAutocompletion(), | 221 user_input_in_progress_, user_text_, view_->GetGrayTextAutocompletion(), |
| 220 keyword_, is_keyword_hint_, | 222 keyword_, is_keyword_hint_, keyword_mode_entry_method_, |
| 221 controller_->GetToolbarModel()->url_replacement_enabled(), | 223 controller_->GetToolbarModel()->url_replacement_enabled(), |
| 222 focus_state_, focus_source_, input_); | 224 focus_state_, focus_source_, input_); |
| 223 } | 225 } |
| 224 | 226 |
| 225 void OmniboxEditModel::RestoreState(const State* state) { | 227 void OmniboxEditModel::RestoreState(const State* state) { |
| 226 // We need to update the permanent text correctly and revert the view | 228 // We need to update the permanent text correctly and revert the view |
| 227 // regardless of whether there is saved state. | 229 // regardless of whether there is saved state. |
| 228 bool url_replacement_enabled = !state || state->url_replacement_enabled; | 230 bool url_replacement_enabled = !state || state->url_replacement_enabled; |
| 229 controller_->GetToolbarModel()->set_url_replacement_enabled( | 231 controller_->GetToolbarModel()->set_url_replacement_enabled( |
| 230 url_replacement_enabled); | 232 url_replacement_enabled); |
| 231 permanent_text_ = controller_->GetToolbarModel()->GetText(); | 233 permanent_text_ = controller_->GetToolbarModel()->GetText(); |
| 232 // Don't muck with the search term replacement state, as we've just set it | 234 // Don't muck with the search term replacement state, as we've just set it |
| 233 // correctly. | 235 // correctly. |
| 234 view_->RevertWithoutResettingSearchTermReplacement(); | 236 view_->RevertWithoutResettingSearchTermReplacement(); |
| 235 // Restore the autocomplete controller's input, or clear it if this is a new | 237 // Restore the autocomplete controller's input, or clear it if this is a new |
| 236 // tab. | 238 // tab. |
| 237 input_ = state ? state->autocomplete_input : AutocompleteInput(); | 239 input_ = state ? state->autocomplete_input : AutocompleteInput(); |
| 238 if (!state) | 240 if (!state) |
| 239 return; | 241 return; |
| 240 | 242 |
| 241 SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH); | 243 SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH); |
| 242 focus_source_ = state->focus_source; | 244 focus_source_ = state->focus_source; |
| 243 // Restore any user editing. | 245 // Restore any user editing. |
| 244 if (state->user_input_in_progress) { | 246 if (state->user_input_in_progress) { |
| 245 // NOTE: Be sure and set keyword-related state AFTER invoking | 247 // NOTE: Be sure to set keyword-related state AFTER invoking |
| 246 // SetUserText(), as SetUserText() clears the keyword state. | 248 // SetUserText(), as SetUserText() clears the keyword state. |
| 247 view_->SetUserText(state->user_text, false); | 249 view_->SetUserText(state->user_text, false); |
| 248 keyword_ = state->keyword; | 250 keyword_ = state->keyword; |
| 249 is_keyword_hint_ = state->is_keyword_hint; | 251 is_keyword_hint_ = state->is_keyword_hint; |
| 252 keyword_mode_entry_method_ = state->keyword_mode_entry_method; |
| 250 view_->SetGrayTextAutocompletion(state->gray_text); | 253 view_->SetGrayTextAutocompletion(state->gray_text); |
| 251 } | 254 } |
| 252 } | 255 } |
| 253 | 256 |
| 254 AutocompleteMatch OmniboxEditModel::CurrentMatch( | 257 AutocompleteMatch OmniboxEditModel::CurrentMatch( |
| 255 GURL* alternate_nav_url) const { | 258 GURL* alternate_nav_url) const { |
| 256 // If we have a valid match use it. Otherwise get one for the current text. | 259 // If we have a valid match use it. Otherwise get one for the current text. |
| 257 AutocompleteMatch match = omnibox_controller_->current_match(); | 260 AutocompleteMatch match = omnibox_controller_->current_match(); |
| 258 | 261 |
| 259 if (!match.destination_url.is_valid()) { | 262 if (!match.destination_url.is_valid()) { |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 618 match.transition = ui::PAGE_TRANSITION_LINK; | 621 match.transition = ui::PAGE_TRANSITION_LINK; |
| 619 } | 622 } |
| 620 | 623 |
| 621 client_->OnInputAccepted(match); | 624 client_->OnInputAccepted(match); |
| 622 | 625 |
| 623 DCHECK(popup_model()); | 626 DCHECK(popup_model()); |
| 624 view_->OpenMatch(match, disposition, alternate_nav_url, base::string16(), | 627 view_->OpenMatch(match, disposition, alternate_nav_url, base::string16(), |
| 625 popup_model()->selected_line()); | 628 popup_model()->selected_line()); |
| 626 } | 629 } |
| 627 | 630 |
| 631 void OmniboxEditModel::EnterKeywordModeForDefaultSearchProvider( |
| 632 KeywordModeEntryMethod entry_method) { |
| 633 autocomplete_controller()->Stop(false); |
| 634 |
| 635 user_input_in_progress_ = true; |
| 636 keyword_ = client_->GetTemplateURLService()-> |
| 637 GetDefaultSearchProvider()->keyword(); |
| 638 is_keyword_hint_ = false; |
| 639 keyword_mode_entry_method_ = entry_method; |
| 640 |
| 641 StartAutocomplete(false, false, true); |
| 642 |
| 643 UMA_HISTOGRAM_ENUMERATION( |
| 644 kEnteredKeywordModeHistogram, static_cast<int>(entry_method), |
| 645 static_cast<int>(KeywordModeEntryMethod::NUM_ITEMS)); |
| 646 } |
| 647 |
| 628 void OmniboxEditModel::OpenMatch(AutocompleteMatch match, | 648 void OmniboxEditModel::OpenMatch(AutocompleteMatch match, |
| 629 WindowOpenDisposition disposition, | 649 WindowOpenDisposition disposition, |
| 630 const GURL& alternate_nav_url, | 650 const GURL& alternate_nav_url, |
| 631 const base::string16& pasted_text, | 651 const base::string16& pasted_text, |
| 632 size_t index) { | 652 size_t index) { |
| 633 const base::TimeTicks& now(base::TimeTicks::Now()); | 653 const base::TimeTicks& now(base::TimeTicks::Now()); |
| 634 base::TimeDelta elapsed_time_since_user_first_modified_omnibox( | 654 base::TimeDelta elapsed_time_since_user_first_modified_omnibox( |
| 635 now - time_user_first_modified_omnibox_); | 655 now - time_user_first_modified_omnibox_); |
| 636 autocomplete_controller()->UpdateMatchDestinationURLWithQueryFormulationTime( | 656 autocomplete_controller()->UpdateMatchDestinationURLWithQueryFormulationTime( |
| 637 elapsed_time_since_user_first_modified_omnibox, &match); | 657 elapsed_time_since_user_first_modified_omnibox, &match); |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 776 match.transition | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)); | 796 match.transition | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)); |
| 777 if (observer && observer->HasSeenPendingLoad()) | 797 if (observer && observer->HasSeenPendingLoad()) |
| 778 ignore_result(observer.release()); // The observer will delete itself. | 798 ignore_result(observer.release()); // The observer will delete itself. |
| 779 } | 799 } |
| 780 | 800 |
| 781 BookmarkModel* bookmark_model = client_->GetBookmarkModel(); | 801 BookmarkModel* bookmark_model = client_->GetBookmarkModel(); |
| 782 if (bookmark_model && bookmark_model->IsBookmarked(match.destination_url)) | 802 if (bookmark_model && bookmark_model->IsBookmarked(match.destination_url)) |
| 783 client_->OnBookmarkLaunched(); | 803 client_->OnBookmarkLaunched(); |
| 784 } | 804 } |
| 785 | 805 |
| 786 bool OmniboxEditModel::AcceptKeyword(EnteredKeywordModeMethod entered_method) { | 806 bool OmniboxEditModel::AcceptKeyword( |
| 807 KeywordModeEntryMethod entry_method) { |
| 787 DCHECK(is_keyword_hint_ && !keyword_.empty()); | 808 DCHECK(is_keyword_hint_ && !keyword_.empty()); |
| 788 | 809 |
| 789 autocomplete_controller()->Stop(false); | 810 autocomplete_controller()->Stop(false); |
| 811 |
| 790 is_keyword_hint_ = false; | 812 is_keyword_hint_ = false; |
| 813 keyword_mode_entry_method_ = entry_method; |
| 814 user_text_ = MaybeStripKeyword(user_text_); |
| 791 | 815 |
| 792 user_text_ = MaybeStripKeyword(user_text_); | 816 user_text_ = MaybeStripKeyword(user_text_); |
| 793 | 817 |
| 794 if (popup_model() && popup_model()->IsOpen()) | 818 if (popup_model() && popup_model()->IsOpen()) |
| 795 popup_model()->SetSelectedLineState(OmniboxPopupModel::KEYWORD); | 819 popup_model()->SetSelectedLineState(OmniboxPopupModel::KEYWORD); |
| 796 else | 820 else |
| 797 StartAutocomplete(false, true, true); | 821 StartAutocomplete(false, true, true); |
| 798 | 822 |
| 799 // When entering keyword mode via tab, the new text to show is whatever the | 823 // When entering keyword mode via tab, the new text to show is whatever the |
| 800 // newly-selected match in the dropdown is. When entering via space, however, | 824 // newly-selected match in the dropdown is. When entering via space, however, |
| 801 // we should make sure to use the actual |user_text_| as the basis for the new | 825 // we should make sure to use the actual |user_text_| as the basis for the new |
| 802 // text. This ensures that if the user types "<keyword><space>" and the | 826 // text. This ensures that if the user types "<keyword><space>" and the |
| 803 // default match would have inline autocompleted a further string (e.g. | 827 // default match would have inline autocompleted a further string (e.g. |
| 804 // because there's a past multi-word search beginning with this keyword), the | 828 // because there's a past multi-word search beginning with this keyword), the |
| 805 // inline autocompletion doesn't get filled in as the keyword search query | 829 // inline autocompletion doesn't get filled in as the keyword search query |
| 806 // text. | 830 // text. |
| 807 // | 831 // |
| 808 // We also treat tabbing into keyword mode like tabbing through the popup in | 832 // We also treat tabbing into keyword mode like tabbing through the popup in |
| 809 // that we set |has_temporary_text_|, whereas pressing space is treated like | 833 // that we set |has_temporary_text_|, whereas pressing space is treated like |
| 810 // a new keystroke that changes the current match instead of overlaying it | 834 // a new keystroke that changes the current match instead of overlaying it |
| 811 // with a temporary one. This is important because rerunning autocomplete | 835 // with a temporary one. This is important because rerunning autocomplete |
| 812 // after the user pressed space, which will have happened just before reaching | 836 // after the user pressed space, which will have happened just before reaching |
| 813 // here, may have generated a new match, which the user won't actually see and | 837 // here, may have generated a new match, which the user won't actually see and |
| 814 // which we don't want to switch back to when existing keyword mode; see | 838 // which we don't want to switch back to when existing keyword mode; see |
| 815 // comments in ClearKeyword(). | 839 // comments in ClearKeyword(). |
| 816 if (entered_method == ENTERED_KEYWORD_MODE_VIA_TAB) { | 840 if (entry_method == KeywordModeEntryMethod::TAB) { |
| 817 // Ensure the current selection is saved before showing keyword mode | 841 // Ensure the current selection is saved before showing keyword mode |
| 818 // so that moving to another line and then reverting the text will restore | 842 // so that moving to another line and then reverting the text will restore |
| 819 // the current state properly. | 843 // the current state properly. |
| 820 bool save_original_selection = !has_temporary_text_; | 844 bool save_original_selection = !has_temporary_text_; |
| 821 has_temporary_text_ = true; | 845 has_temporary_text_ = true; |
| 822 const AutocompleteMatch& match = CurrentMatch(NULL); | 846 const AutocompleteMatch& match = CurrentMatch(NULL); |
| 823 original_url_ = match.destination_url; | 847 original_url_ = match.destination_url; |
| 824 view_->OnTemporaryTextMaybeChanged( | 848 view_->OnTemporaryTextMaybeChanged( |
| 825 MaybeStripKeyword(match.fill_into_edit), save_original_selection, | 849 MaybeStripKeyword(match.fill_into_edit), save_original_selection, |
| 826 true); | 850 true); |
| 827 } else { | 851 } else { |
| 828 view_->OnTemporaryTextMaybeChanged(user_text_, false, true); | 852 view_->OnTemporaryTextMaybeChanged(user_text_, false, true); |
| 829 } | 853 } |
| 830 | 854 |
| 831 base::RecordAction(base::UserMetricsAction("AcceptedKeywordHint")); | 855 base::RecordAction(base::UserMetricsAction("AcceptedKeywordHint")); |
| 832 UMA_HISTOGRAM_ENUMERATION(kEnteredKeywordModeHistogram, entered_method, | 856 UMA_HISTOGRAM_ENUMERATION( |
| 833 ENTERED_KEYWORD_MODE_NUM_ITEMS); | 857 kEnteredKeywordModeHistogram, static_cast<int>(entry_method), |
| 858 static_cast<int>(KeywordModeEntryMethod::NUM_ITEMS)); |
| 834 | 859 |
| 835 return true; | 860 return true; |
| 836 } | 861 } |
| 837 | 862 |
| 838 void OmniboxEditModel::AcceptTemporaryTextAsUserText() { | 863 void OmniboxEditModel::AcceptTemporaryTextAsUserText() { |
| 839 InternalSetUserText(view_->GetText()); | 864 InternalSetUserText(view_->GetText()); |
| 840 has_temporary_text_ = false; | 865 has_temporary_text_ = false; |
| 841 | 866 |
| 842 if (user_input_in_progress_ || !in_revert_) | 867 if (user_input_in_progress_ || !in_revert_) |
| 843 client_->OnInputStateChanged(); | 868 client_->OnInputStateChanged(); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 907 // without re-enabling keyword mode. The common case is state 3, where | 932 // without re-enabling keyword mode. The common case is state 3, where |
| 908 // the user entered keyword mode unintentionally, so backspacing | 933 // the user entered keyword mode unintentionally, so backspacing |
| 909 // immediately out of keyword mode should keep the space. In states 1 and | 934 // immediately out of keyword mode should keep the space. In states 1 and |
| 910 // 2, having the space is "safer" behavior. For instance, if the user types | 935 // 2, having the space is "safer" behavior. For instance, if the user types |
| 911 // "google.com f" or "google.com<tab>f" in the omnibox, moves the cursor to | 936 // "google.com f" or "google.com<tab>f" in the omnibox, moves the cursor to |
| 912 // the left, and presses backspace to leave keyword mode (state 1), it's | 937 // the left, and presses backspace to leave keyword mode (state 1), it's |
| 913 // better to have the space because it may be what the user wanted. The | 938 // better to have the space because it may be what the user wanted. The |
| 914 // user can easily delete it. On the other hand, if there is no space and | 939 // user can easily delete it. On the other hand, if there is no space and |
| 915 // the user wants it, it's more work to add because typing the space will | 940 // the user wants it, it's more work to add because typing the space will |
| 916 // enter keyword mode, which then the user would have to leave again. | 941 // enter keyword mode, which then the user would have to leave again. |
| 917 const base::string16 window_text = | 942 |
| 918 keyword_ + base::ASCIIToUTF16(" ") + view_->GetText(); | 943 // If we entered keyword mode in a special way like using a keyboard |
| 919 view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length() + 1, | 944 // shortcut or typing a question mark in a blank omnibox, don't restore the |
| 920 false, false); | 945 // keyword. Instead, restore the question mark iff the user originally |
| 946 // typed one. |
| 947 base::string16 prefix; |
| 948 if (keyword_mode_entry_method_ == KeywordModeEntryMethod::QUESTION_MARK) |
| 949 prefix = base::ASCIIToUTF16("?"); |
| 950 else if (keyword_mode_entry_method_ != |
| 951 KeywordModeEntryMethod::KEYBOARD_SHORTCUT) |
| 952 prefix = keyword_ + base::ASCIIToUTF16(" "); |
| 953 |
| 921 keyword_.clear(); | 954 keyword_.clear(); |
| 922 is_keyword_hint_ = false; | 955 is_keyword_hint_ = false; |
| 956 |
| 957 view_->SetWindowTextAndCaretPos(prefix + view_->GetText(), prefix.length(), |
| 958 false, false); |
| 959 |
| 923 view_->OnAfterPossibleChange(false); | 960 view_->OnAfterPossibleChange(false); |
| 924 } | 961 } |
| 925 } | 962 } |
| 926 | 963 |
| 927 void OmniboxEditModel::OnSetFocus(bool control_down) { | 964 void OmniboxEditModel::OnSetFocus(bool control_down) { |
| 928 last_omnibox_focus_ = base::TimeTicks::Now(); | 965 last_omnibox_focus_ = base::TimeTicks::Now(); |
| 929 user_input_since_focus_ = false; | 966 user_input_since_focus_ = false; |
| 930 | 967 |
| 931 // If the omnibox lost focus while the caret was hidden and then regained | 968 // If the omnibox lost focus while the caret was hidden and then regained |
| 932 // focus, OnSetFocus() is called and should restore visibility. Note that | 969 // focus, OnSetFocus() is called and should restore visibility. Note that |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1146 RevertTemporaryText(false); | 1183 RevertTemporaryText(false); |
| 1147 call_controller_onchanged = false; | 1184 call_controller_onchanged = false; |
| 1148 } | 1185 } |
| 1149 | 1186 |
| 1150 // We need to invoke OnChanged in case the destination url changed (as could | 1187 // We need to invoke OnChanged in case the destination url changed (as could |
| 1151 // happen when control is toggled). | 1188 // happen when control is toggled). |
| 1152 if (call_controller_onchanged) | 1189 if (call_controller_onchanged) |
| 1153 OnChanged(); | 1190 OnChanged(); |
| 1154 } | 1191 } |
| 1155 | 1192 |
| 1156 bool OmniboxEditModel::OnAfterPossibleChange(const base::string16& old_text, | 1193 bool OmniboxEditModel::OnAfterPossibleChange(const TextStateChange& text_change, |
| 1157 const base::string16& new_text, | |
| 1158 size_t selection_start, | |
| 1159 size_t selection_end, | |
| 1160 bool selection_differs, | |
| 1161 bool text_differs, | |
| 1162 bool just_deleted_text, | |
| 1163 bool allow_keyword_ui_change) { | 1194 bool allow_keyword_ui_change) { |
| 1164 // Update the paste state as appropriate: if we're just finishing a paste | 1195 // Update the paste state as appropriate: if we're just finishing a paste |
| 1165 // that replaced all the text, preserve that information; otherwise, if we've | 1196 // that replaced all the text, preserve that information; otherwise, if we've |
| 1166 // made some other edit, clear paste tracking. | 1197 // made some other edit, clear paste tracking. |
| 1167 if (paste_state_ == PASTING) | 1198 if (paste_state_ == PASTING) |
| 1168 paste_state_ = PASTED; | 1199 paste_state_ = PASTED; |
| 1169 else if (text_differs) | 1200 else if (text_change.text_differs) |
| 1170 paste_state_ = NONE; | 1201 paste_state_ = NONE; |
| 1171 | 1202 |
| 1172 if (text_differs || selection_differs) { | 1203 if (text_change.text_differs || text_change.selection_differs) { |
| 1173 // Record current focus state for this input if we haven't already. | 1204 // Record current focus state for this input if we haven't already. |
| 1174 if (focus_source_ == INVALID) { | 1205 if (focus_source_ == INVALID) { |
| 1175 // We should generally expect the omnibox to have focus at this point, but | 1206 // We should generally expect the omnibox to have focus at this point, but |
| 1176 // it doesn't always on Linux. This is because, unlike other platforms, | 1207 // it doesn't always on Linux. This is because, unlike other platforms, |
| 1177 // right clicking in the omnibox on Linux doesn't focus it. So pasting via | 1208 // right clicking in the omnibox on Linux doesn't focus it. So pasting via |
| 1178 // right-click can change the contents without focusing the omnibox. | 1209 // right-click can change the contents without focusing the omnibox. |
| 1179 // TODO(samarth): fix Linux focus behavior and add a DCHECK here to | 1210 // TODO(samarth): fix Linux focus behavior and add a DCHECK here to |
| 1180 // check that the omnibox does have focus. | 1211 // check that the omnibox does have focus. |
| 1181 focus_source_ = (focus_state_ == OMNIBOX_FOCUS_INVISIBLE) ? | 1212 focus_source_ = |
| 1182 FAKEBOX : OMNIBOX; | 1213 (focus_state_ == OMNIBOX_FOCUS_INVISIBLE) ? FAKEBOX : OMNIBOX; |
| 1183 } | 1214 } |
| 1184 | 1215 |
| 1185 // Restore caret visibility whenever the user changes text or selection in | 1216 // Restore caret visibility whenever the user changes text or selection in |
| 1186 // the omnibox. | 1217 // the omnibox. |
| 1187 SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_TYPING); | 1218 SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_TYPING); |
| 1188 } | 1219 } |
| 1189 | 1220 |
| 1190 // Modifying the selection counts as accepting the autocompleted text. | 1221 // Modifying the selection counts as accepting the autocompleted text. |
| 1191 const bool user_text_changed = | 1222 const bool user_text_changed = |
| 1192 text_differs || (selection_differs && !inline_autocomplete_text_.empty()); | 1223 text_change.text_differs || |
| 1224 (text_change.selection_differs && !inline_autocomplete_text_.empty()); |
| 1193 | 1225 |
| 1194 // If something has changed while the control key is down, prevent | 1226 // If something has changed while the control key is down, prevent |
| 1195 // "ctrl-enter" until the control key is released. | 1227 // "ctrl-enter" until the control key is released. |
| 1196 if ((text_differs || selection_differs) && | 1228 if ((text_change.text_differs || text_change.selection_differs) && |
| 1197 (control_key_state_ == DOWN_WITHOUT_CHANGE)) | 1229 (control_key_state_ == DOWN_WITHOUT_CHANGE)) |
| 1198 control_key_state_ = DOWN_WITH_CHANGE; | 1230 control_key_state_ = DOWN_WITH_CHANGE; |
| 1199 | 1231 |
| 1200 if (!user_text_changed) | 1232 if (!user_text_changed) { |
| 1201 return false; | 1233 if (text_change.keyword_differs) { |
| 1234 // We won't need the below logic for creating a keyword by a space at the |
| 1235 // end or in the middle, or by typing a '?', but we do need to update the |
| 1236 // popup view because the keyword can change without the text changing, |
| 1237 // for example when the keyword is "youtube.com" and the user presses |
| 1238 // ctrl-k to change it to "google.com", or if the user text is empty and |
| 1239 // the user presses backspace. |
| 1240 view_->UpdatePopup(); |
| 1241 } |
| 1242 return text_change.keyword_differs; |
| 1243 } |
| 1202 | 1244 |
| 1203 // If the user text has not changed, we do not want to change the model's | 1245 // If the user text has not changed, we do not want to change the model's |
| 1204 // state associated with the text. Otherwise, we can get surprising behavior | 1246 // state associated with the text. Otherwise, we can get surprising behavior |
| 1205 // where the autocompleted text unexpectedly reappears, e.g. crbug.com/55983 | 1247 // where the autocompleted text unexpectedly reappears, e.g. crbug.com/55983 |
| 1206 InternalSetUserText(new_text); | 1248 InternalSetUserText(*text_change.new_text); |
| 1207 has_temporary_text_ = false; | 1249 has_temporary_text_ = false; |
| 1208 | 1250 |
| 1209 // Track when the user has deleted text so we won't allow inline | 1251 // Track when the user has deleted text so we won't allow inline |
| 1210 // autocomplete. | 1252 // autocomplete. |
| 1211 just_deleted_text_ = just_deleted_text; | 1253 just_deleted_text_ = text_change.just_deleted_text; |
| 1212 | 1254 |
| 1213 if (user_input_in_progress_ && user_text_.empty()) { | 1255 if (user_input_in_progress_ && user_text_.empty()) { |
| 1214 // Log cases where the user started editing and then subsequently cleared | 1256 // Log cases where the user started editing and then subsequently cleared |
| 1215 // all the text. Note that this explicitly doesn't catch cases like | 1257 // all the text. Note that this explicitly doesn't catch cases like |
| 1216 // "hit ctrl-l to select whole edit contents, then hit backspace", because | 1258 // "hit ctrl-l to select whole edit contents, then hit backspace", because |
| 1217 // in such cases, |user_input_in_progress| won't be true here. | 1259 // in such cases, |user_input_in_progress| won't be true here. |
| 1218 UMA_HISTOGRAM_ENUMERATION(kOmniboxUserTextClearedHistogram, | 1260 UMA_HISTOGRAM_ENUMERATION(kOmniboxUserTextClearedHistogram, |
| 1219 OMNIBOX_USER_TEXT_CLEARED_BY_EDITING, | 1261 OMNIBOX_USER_TEXT_CLEARED_BY_EDITING, |
| 1220 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS); | 1262 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS); |
| 1221 } | 1263 } |
| 1222 | 1264 |
| 1223 const bool no_selection = selection_start == selection_end; | 1265 const bool no_selection = |
| 1266 text_change.new_sel_start == text_change.new_sel_end; |
| 1224 | 1267 |
| 1225 // Update the popup for the change, in the process changing to keyword mode | 1268 // Update the popup for the change, in the process changing to keyword mode |
| 1226 // if the user hit space in mid-string after a keyword. | 1269 // if the user hit space in mid-string after a keyword. |
| 1227 // |allow_exact_keyword_match_| will be used by StartAutocomplete() method, | 1270 // |allow_exact_keyword_match_| will be used by StartAutocomplete() method, |
| 1228 // which will be called by |view_->UpdatePopup()|; so after that returns we | 1271 // which will be called by |view_->UpdatePopup()|; so after that returns we |
| 1229 // can safely reset this flag. | 1272 // can safely reset this flag. |
| 1230 allow_exact_keyword_match_ = text_differs && allow_keyword_ui_change && | 1273 allow_exact_keyword_match_ = |
| 1231 !just_deleted_text && no_selection && | 1274 text_change.text_differs && allow_keyword_ui_change && |
| 1232 CreatedKeywordSearchByInsertingSpaceInMiddle(old_text, user_text_, | 1275 !text_change.just_deleted_text && no_selection && |
| 1233 selection_start); | 1276 CreatedKeywordSearchByInsertingSpaceInMiddle( |
| 1277 *text_change.old_text, user_text_, text_change.new_sel_start); |
| 1234 view_->UpdatePopup(); | 1278 view_->UpdatePopup(); |
| 1235 if (allow_exact_keyword_match_) { | 1279 if (allow_exact_keyword_match_) { |
| 1236 UMA_HISTOGRAM_ENUMERATION(kEnteredKeywordModeHistogram, | 1280 keyword_mode_entry_method_ = KeywordModeEntryMethod::SPACE_IN_MIDDLE; |
| 1237 ENTERED_KEYWORD_MODE_VIA_SPACE_IN_MIDDLE, | 1281 UMA_HISTOGRAM_ENUMERATION( |
| 1238 ENTERED_KEYWORD_MODE_NUM_ITEMS); | 1282 kEnteredKeywordModeHistogram, |
| 1283 static_cast<int>(KeywordModeEntryMethod::SPACE_IN_MIDDLE), |
| 1284 static_cast<int>(KeywordModeEntryMethod::NUM_ITEMS)); |
| 1239 allow_exact_keyword_match_ = false; | 1285 allow_exact_keyword_match_ = false; |
| 1240 } | 1286 } |
| 1241 | 1287 |
| 1288 if (!text_change.text_differs || !allow_keyword_ui_change || |
| 1289 (text_change.just_deleted_text && no_selection) || |
| 1290 is_keyword_selected() || (paste_state_ != NONE)) |
| 1291 return true; |
| 1292 |
| 1293 // If the user input a "?" at the beginning of the text, put them into |
| 1294 // keyword mode for their default search provider. |
| 1295 if ((text_change.new_sel_start == 1) && (user_text_[0] == '?')) { |
| 1296 view_->SetUserText(user_text_.substr(1)); |
| 1297 EnterKeywordModeForDefaultSearchProvider( |
| 1298 KeywordModeEntryMethod::QUESTION_MARK); |
| 1299 // Set the caret position to 0 without changing the user text. |
| 1300 view_->SetWindowTextAndCaretPos(view_->GetText(), 0, false, false); |
| 1301 return false; |
| 1302 } |
| 1303 |
| 1242 // Change to keyword mode if the user is now pressing space after a keyword | 1304 // Change to keyword mode if the user is now pressing space after a keyword |
| 1243 // name. Note that if this is the case, then even if there was no keyword | 1305 // name. Note that if this is the case, then even if there was no keyword |
| 1244 // hint when we entered this function (e.g. if the user has used space to | 1306 // hint when we entered this function (e.g. if the user has used space to |
| 1245 // replace some selected text that was adjoined to this keyword), there will | 1307 // replace some selected text that was adjoined to this keyword), there will |
| 1246 // be one now because of the call to UpdatePopup() above; so it's safe for | 1308 // be one now because of the call to UpdatePopup() above; so it's safe for |
| 1247 // MaybeAcceptKeywordBySpace() to look at |keyword_| and |is_keyword_hint_| to | 1309 // MaybeAcceptKeywordBySpace() to look at |keyword_| and |is_keyword_hint_| |
| 1248 // determine what keyword, if any, is applicable. | 1310 // to determine what keyword, if any, is applicable. |
| 1249 // | 1311 // |
| 1250 // If MaybeAcceptKeywordBySpace() accepts the keyword and returns true, that | 1312 // If MaybeAcceptKeywordBySpace() accepts the keyword and returns true, that |
| 1251 // will have updated our state already, so in that case we don't also return | 1313 // will have updated our state already, so in that case we don't also return |
| 1252 // true from this function. | 1314 // true from this function. |
| 1253 return !(text_differs && allow_keyword_ui_change && !just_deleted_text && | 1315 return (text_change.new_sel_start != user_text_.length()) || |
| 1254 no_selection && (selection_start == user_text_.length()) && | 1316 !MaybeAcceptKeywordBySpace(user_text_); |
| 1255 MaybeAcceptKeywordBySpace(user_text_)); | |
| 1256 } | 1317 } |
| 1257 | 1318 |
| 1258 // TODO(beaudoin): Merge OnPopupDataChanged with this method once the popup | 1319 // TODO(beaudoin): Merge OnPopupDataChanged with this method once the popup |
| 1259 // handling has completely migrated to omnibox_controller. | 1320 // handling has completely migrated to omnibox_controller. |
| 1260 void OmniboxEditModel::OnCurrentMatchChanged() { | 1321 void OmniboxEditModel::OnCurrentMatchChanged() { |
| 1261 has_temporary_text_ = false; | 1322 has_temporary_text_ = false; |
| 1262 | 1323 |
| 1263 const AutocompleteMatch& match = omnibox_controller_->current_match(); | 1324 const AutocompleteMatch& match = omnibox_controller_->current_match(); |
| 1264 | 1325 |
| 1265 client_->OnCurrentMatchChanged(match); | 1326 client_->OnCurrentMatchChanged(match); |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1369 has_temporary_text_ = false; | 1430 has_temporary_text_ = false; |
| 1370 | 1431 |
| 1371 if (revert_popup && popup_model()) | 1432 if (revert_popup && popup_model()) |
| 1372 popup_model()->ResetToDefaultMatch(); | 1433 popup_model()->ResetToDefaultMatch(); |
| 1373 view_->OnRevertTemporaryText(); | 1434 view_->OnRevertTemporaryText(); |
| 1374 } | 1435 } |
| 1375 | 1436 |
| 1376 bool OmniboxEditModel::MaybeAcceptKeywordBySpace( | 1437 bool OmniboxEditModel::MaybeAcceptKeywordBySpace( |
| 1377 const base::string16& new_text) { | 1438 const base::string16& new_text) { |
| 1378 size_t keyword_length = new_text.length() - 1; | 1439 size_t keyword_length = new_text.length() - 1; |
| 1379 return (paste_state_ == NONE) && is_keyword_hint_ && | 1440 return is_keyword_hint_ && (keyword_.length() == keyword_length) && |
| 1380 (keyword_.length() == keyword_length) && | 1441 IsSpaceCharForAcceptingKeyword(new_text[keyword_length]) && |
| 1381 IsSpaceCharForAcceptingKeyword(new_text[keyword_length]) && | 1442 !new_text.compare(0, keyword_length, keyword_, 0, keyword_length) && |
| 1382 !new_text.compare(0, keyword_length, keyword_, 0, keyword_length) && | 1443 AcceptKeyword(KeywordModeEntryMethod::SPACE_AT_END); |
| 1383 AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_SPACE_AT_END); | |
| 1384 } | 1444 } |
| 1385 | 1445 |
| 1386 bool OmniboxEditModel::CreatedKeywordSearchByInsertingSpaceInMiddle( | 1446 bool OmniboxEditModel::CreatedKeywordSearchByInsertingSpaceInMiddle( |
| 1387 const base::string16& old_text, | 1447 const base::string16& old_text, |
| 1388 const base::string16& new_text, | 1448 const base::string16& new_text, |
| 1389 size_t caret_position) const { | 1449 size_t caret_position) const { |
| 1390 DCHECK_GE(new_text.length(), caret_position); | 1450 DCHECK_GE(new_text.length(), caret_position); |
| 1391 | 1451 |
| 1392 // Check simple conditions first. | 1452 // Check simple conditions first. |
| 1393 if ((paste_state_ != NONE) || (caret_position < 2) || | 1453 if ((paste_state_ != NONE) || (caret_position < 2) || |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1467 // Update state and notify view if the omnibox has focus and the caret | 1527 // Update state and notify view if the omnibox has focus and the caret |
| 1468 // visibility changed. | 1528 // visibility changed. |
| 1469 const bool was_caret_visible = is_caret_visible(); | 1529 const bool was_caret_visible = is_caret_visible(); |
| 1470 focus_state_ = state; | 1530 focus_state_ = state; |
| 1471 if (focus_state_ != OMNIBOX_FOCUS_NONE && | 1531 if (focus_state_ != OMNIBOX_FOCUS_NONE && |
| 1472 is_caret_visible() != was_caret_visible) | 1532 is_caret_visible() != was_caret_visible) |
| 1473 view_->ApplyCaretVisibility(); | 1533 view_->ApplyCaretVisibility(); |
| 1474 | 1534 |
| 1475 client_->OnFocusChanged(focus_state_, reason); | 1535 client_->OnFocusChanged(focus_state_, reason); |
| 1476 } | 1536 } |
| OLD | NEW |