|  | // Copyright 2012 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_EDIT_MODEL_H_ | 
|  | #define CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_EDIT_MODEL_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <string_view> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/gtest_prod_util.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/memory/safety_checks.h" | 
|  | #include "base/memory/weak_ptr.h" | 
|  | #include "base/observer_list_types.h" | 
|  | #include "base/time/time.h" | 
|  | #include "chrome/browser/ui/omnibox/omnibox_view.h" | 
|  | #include "components/omnibox/browser/autocomplete_controller.h" | 
|  | #include "components/omnibox/browser/autocomplete_input.h" | 
|  | #include "components/omnibox/browser/autocomplete_match.h" | 
|  | #include "components/omnibox/browser/omnibox.mojom-shared.h" | 
|  | #include "components/omnibox/browser/omnibox_popup_selection.h" | 
|  | #include "components/omnibox/common/omnibox_focus_state.h" | 
|  | #include "third_party/metrics_proto/omnibox_event.pb.h" | 
|  | #include "third_party/skia/include/core/SkColor.h" | 
|  | #include "ui/base/window_open_disposition.h" | 
|  | #include "ui/gfx/native_ui_types.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | class OmniboxController; | 
|  | class OmniboxPopupView; | 
|  | namespace gfx { | 
|  | class Image; | 
|  | } | 
|  |  | 
|  | class OmniboxEditModel { | 
|  | // TODO(crbug.com/392015004): Remove this macro once it gets fixed. | 
|  | ADVANCED_MEMORY_SAFETY_CHECKS(); | 
|  |  | 
|  | public: | 
|  | struct State { | 
|  | State(bool user_input_in_progress, | 
|  | const std::u16string& user_text, | 
|  | const std::u16string& keyword, | 
|  | const std::u16string& keyword_placeholder, | 
|  | bool is_keyword_hint, | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod | 
|  | keyword_mode_entry_method, | 
|  | OmniboxFocusState focus_state, | 
|  | const AutocompleteInput& autocomplete_input); | 
|  | State(const State& other); | 
|  | State& operator=(const State&) = delete; | 
|  | ~State(); | 
|  |  | 
|  | bool user_input_in_progress; | 
|  | const std::u16string user_text; | 
|  | const std::u16string keyword; | 
|  | const std::u16string keyword_placeholder; | 
|  | const bool is_keyword_hint; | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod | 
|  | keyword_mode_entry_method; | 
|  | OmniboxFocusState focus_state; | 
|  | const AutocompleteInput autocomplete_input; | 
|  | }; | 
|  |  | 
|  | // Intended for view code to update itself when the model changes. Most view | 
|  | // updates are propagated by calling methods on `view_` & `popup_view_` | 
|  | // directly. But it's possible to have multiple views interested in being | 
|  | // updates (e.g. when the side-by-side debugging feature | 
|  | // `kWebUIOmniboxPopupDebug` is enabled). So updates that multiple views might | 
|  | // be interested in use the observer pattern. In the future, perhaps all | 
|  | // model->view updates can go through the observer. | 
|  | class Observer : public base::CheckedObserver { | 
|  | public: | 
|  | // Invoked when the selection changes. The `line` field in either selection | 
|  | // may be `OmniboxPopupSelection::kNoMatch`. | 
|  | virtual void OnSelectionChanged(OmniboxPopupSelection old_selection, | 
|  | OmniboxPopupSelection new_selection) = 0; | 
|  |  | 
|  | // Invoked when the icon used for the given match has been updated. | 
|  | virtual void OnMatchIconUpdated(size_t index) = 0; | 
|  |  | 
|  | ~Observer() override = default; | 
|  | }; | 
|  |  | 
|  | OmniboxEditModel(OmniboxController* controller, OmniboxView* view); | 
|  | OmniboxEditModel(const OmniboxEditModel&) = delete; | 
|  | OmniboxEditModel& operator=(const OmniboxEditModel&) = delete; | 
|  | virtual ~OmniboxEditModel(); | 
|  |  | 
|  | void set_popup_view(OmniboxPopupView* popup_view); | 
|  | void AddObserver(Observer* observer); | 
|  | void RemoveObserver(Observer* observer); | 
|  |  | 
|  | metrics::OmniboxEventProto::PageClassification GetPageClassification() const; | 
|  |  | 
|  | // Returns the current state.  This assumes we are switching tabs, and changes | 
|  | // the internal state appropriately. | 
|  | State GetStateForTabSwitch() const; | 
|  |  | 
|  | // Resets the tab state, then restores local state from |state|. |state| may | 
|  | // be nullptr if there is no saved state. | 
|  | void RestoreState(const State* state); | 
|  |  | 
|  | // Returns the match for the current text. If the user has not edited the text | 
|  | // this is the match corresponding to the permanent text. Returns the | 
|  | // alternate nav URL, if |alternate_nav_url| is non-NULL and there is such a | 
|  | // URL. Virtual for testing. | 
|  | virtual AutocompleteMatch CurrentMatchAndAlternateNavUrl( | 
|  | GURL* alternate_nav_url) const; | 
|  |  | 
|  | // Provided for convenience, since most CurrentMatchAndAlternateNavUrl() | 
|  | // callers do not need the alternate navigation URL. | 
|  | AutocompleteMatch CurrentMatch() const { | 
|  | return CurrentMatchAndAlternateNavUrl(nullptr); | 
|  | } | 
|  |  | 
|  | // Called when the user wants to export the entire current text as a URL. | 
|  | // Sets the url, and if known, the title and favicon. | 
|  | void GetDataForURLExport(GURL* url, | 
|  | std::u16string* title, | 
|  | gfx::Image* favicon); | 
|  |  | 
|  | // Returns true if the current edit contents will be treated as a | 
|  | // URL/navigation, as opposed to a search. | 
|  | bool CurrentTextIsURL() const; | 
|  |  | 
|  | // Adjusts the copied text before writing it to the clipboard. If the copied | 
|  | // text is a URL with the scheme elided, this method reattaches the scheme. | 
|  | // Copied text that looks like a search query will not be modified. | 
|  | // | 
|  | // |sel_min| gives the minimum of the selection, e.g. min(sel_start, sel_end). | 
|  | // |text| is the currently selected text, and may be modified by this method. | 
|  | // |url_from_text| is the GURL interpretation of the selected text, and may | 
|  | // be used for drag-and-drop models or writing hyperlink data types to | 
|  | // system clipboards. | 
|  | // | 
|  | // If the copied text is interpreted as a URL: | 
|  | //  - |write_url| is set to true. | 
|  | //  - |url_from_text| is set to the URL. | 
|  | //  - |text| is set to the URL's spec. The output will be pure ASCII and | 
|  | //    %-escaped, since canonical URLs are always encoded to ASCII. | 
|  | // | 
|  | // If the copied text is *NOT* interpreted as a URL: | 
|  | //  - |write_url| is set to false. | 
|  | //  - |url_from_text| may be modified, but might not contain a valid GURL. | 
|  | //  - |text| is full UTF-16 and not %-escaped. This is because we are not | 
|  | //    interpreting |text| as a URL, so we leave the Unicode characters as-is. | 
|  | void AdjustTextForCopy(int sel_min, | 
|  | std::u16string* text, | 
|  | GURL* url_from_text, | 
|  | bool* write_url); | 
|  |  | 
|  | bool user_input_in_progress() const { return user_input_in_progress_; } | 
|  |  | 
|  | std::u16string user_text() const { return user_text_; } | 
|  |  | 
|  | // Encapsulates all the varied conditions for whether to override the | 
|  | // permanent page icon (associated with the currently displayed page), | 
|  | // with a temporary icon (associated with the current match or user text). | 
|  | bool ShouldShowCurrentPageIcon() const; | 
|  |  | 
|  | // Returns the SuperGIcon for chrome builds. Otherwise return an empty | 
|  | // ImageModel. If `dark_mode` is enabled, return the monochrome version of the | 
|  | // icon. | 
|  | ui::ImageModel GetSuperGIcon(int image_size, bool dark_mode) const; | 
|  |  | 
|  | // Whether the "Add Context" button should be shown in place of the location | 
|  | // bar page info icon button. | 
|  | bool ShouldShowAddContextButton() const; | 
|  |  | 
|  | // Returns the "mega plus" icon associated with the "Add Context" button. | 
|  | ui::ImageModel GetAddContextIcon(int image_size) const; | 
|  |  | 
|  | // Returns the Agentspace icon for chrome builds. Otherwise return an empty | 
|  | // Image. If `dark_mode` is enabled, return the monochrome version of the | 
|  | // icon. | 
|  | gfx::Image GetAgentspaceIcon(bool dark_mode) const; | 
|  |  | 
|  | // Sets the state of user_input_in_progress_, and notifies the observer if | 
|  | // that state has changed. | 
|  | void SetInputInProgress(bool in_progress); | 
|  |  | 
|  | // Calls SetInputInProgress, via SetInputInProgressNoNotify and | 
|  | // NotifyObserversInputInProgress, calling the latter after | 
|  | // StartAutocomplete, so that the result is only updated once. | 
|  | void UpdateInput(bool has_selected_text, bool prevent_inline_autocomplete); | 
|  |  | 
|  | // Resets the permanent display texts (display_text_ and url_for_editing_) | 
|  | // to those provided by the controller. Returns true if the display texts | 
|  | // have changed and the change should be immediately user-visible, because | 
|  | // either the user is not editing or the edit does not have focus. | 
|  | bool ResetDisplayTexts(); | 
|  |  | 
|  | // Returns the permanent display text for the current page and Omnibox state. | 
|  | std::u16string GetPermanentDisplayText() const; | 
|  |  | 
|  | // Sets the user_text_ to |text|. Also enters user-input-in-progress mode. | 
|  | // Virtual for testing. | 
|  | virtual void SetUserText(const std::u16string& text); | 
|  |  | 
|  | // If the omnibox is currently displaying elided text, this method will | 
|  | // restore the full URL into the user text. After unelision, this selects-all, | 
|  | // enters user-input-in-progress mode, and then returns true. | 
|  | // | 
|  | // If the omnibox is not currently displaying elided text, this method will | 
|  | // no-op and return false. | 
|  | bool Unelide(); | 
|  |  | 
|  | // Invoked any time the text may have changed in the edit. Notifies the | 
|  | // controller. | 
|  | void OnChanged(); | 
|  |  | 
|  | // Reverts the edit model back to its unedited state (permanent text showing, | 
|  | // no user input in progress). | 
|  | void Revert(); | 
|  |  | 
|  | // Directs the popup to start autocomplete.  Makes use of the |view_| text and | 
|  | // selection, so make sure to set those before calling StartAutocomplete(). | 
|  | void StartAutocomplete(bool has_selected_text, | 
|  | bool prevent_inline_autocomplete); | 
|  |  | 
|  | // Determines whether the user can "paste and go", given the specified text. | 
|  | bool CanPasteAndGo(const std::u16string& text) const; | 
|  |  | 
|  | // Navigates to the destination last supplied to CanPasteAndGo. | 
|  | void PasteAndGo( | 
|  | const std::u16string& text, | 
|  | base::TimeTicks match_selection_timestamp = base::TimeTicks()); | 
|  |  | 
|  | // Sets |match| and |alternate_nav_url| based on classifying |text|. | 
|  | // |alternate_nav_url| may be nullptr. | 
|  | void ClassifyString(const std::u16string& text, | 
|  | AutocompleteMatch* match, | 
|  | GURL* alternate_nav_url) const; | 
|  |  | 
|  | // Navigates to AI Mode, with the contents of the currently selected match, if | 
|  | // any. | 
|  | // `via_keyboard` is set to `true` if AI Mode was invoked via keyboard event | 
|  | // and is set to `false` if AI Mode was invoked via mouse / gesture event. | 
|  | void OpenAiMode(bool via_keyboard); | 
|  |  | 
|  | // Returns true if the popup is open and is in in AI-Mode. | 
|  | bool PopupInAiMode() const; | 
|  |  | 
|  | // Opens given selection. Most kinds of selection invoke an action or | 
|  | // otherwise call `OpenMatch`, but some may `AcceptInput` which is not | 
|  | // guaranteed to open a match or commit the omnibox. | 
|  | // `via_keyboard` is set to `true` if the selection was opened due to a | 
|  | // keyboard event and is set to `false` if the selection was opened due | 
|  | // to a mouse / gesture event. | 
|  | void OpenSelection( | 
|  | OmniboxPopupSelection selection, | 
|  | base::TimeTicks timestamp = base::TimeTicks(), | 
|  | WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB, | 
|  | bool via_keyboard = false); | 
|  |  | 
|  | // A simplified version of `OpenSelection()` that opens the model's current | 
|  | // selection. | 
|  | void OpenSelectionForTesting( | 
|  | base::TimeTicks timestamp = base::TimeTicks(), | 
|  | WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB, | 
|  | bool via_keyboard = false); | 
|  |  | 
|  | OmniboxFocusState focus_state() const { return focus_state_; } | 
|  | bool has_focus() const { return focus_state_ != OMNIBOX_FOCUS_NONE; } | 
|  |  | 
|  | base::TimeTicks last_omnibox_focus() const { return last_omnibox_focus_; } | 
|  |  | 
|  | // This is the same as when the Omnibox is visibly focused. | 
|  | bool is_caret_visible() const { | 
|  | return focus_state_ == OMNIBOX_FOCUS_VISIBLE; | 
|  | } | 
|  |  | 
|  | // Accessors for keyword-related state (see comments on `keyword_`, | 
|  | // `keyword_placeholder_` and `is_keyword_hint_`). | 
|  | const std::u16string& keyword() const { return keyword_; } | 
|  | const std::u16string& keyword_placeholder() const { | 
|  | return keyword_placeholder_; | 
|  | } | 
|  | bool is_keyword_hint() const { return is_keyword_hint_; } | 
|  | bool is_keyword_selected() const { | 
|  | return !is_keyword_hint_ && !keyword_.empty(); | 
|  | } | 
|  |  | 
|  | // Accepts the current keyword hint as a keyword. It always returns true for | 
|  | // caller convenience. |entry_method| indicates how the user entered | 
|  | // keyword mode. | 
|  | bool AcceptKeyword( | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod entry_method); | 
|  |  | 
|  | // Sets the current keyword to that of the given `template_url` and updates | 
|  | // the view so the user sees the keyword chip in the omnibox.  Adjusts | 
|  | // user_text_ and the selection based on the display text and the keyword | 
|  | // entry method. Note, the default match may be updated in this process | 
|  | // so the match must support this keyword mode or it will be exited. | 
|  | void EnterKeywordMode( | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod entry_method, | 
|  | const TemplateURL* template_url, | 
|  | const std::u16string& placeholder_text); | 
|  |  | 
|  | // Enters keyword mode for user's default search provider, if enabled. | 
|  | void EnterKeywordModeForDefaultSearchProvider( | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod entry_method); | 
|  |  | 
|  | // Accepts the current temporary text as the user text. | 
|  | void AcceptTemporaryTextAsUserText(); | 
|  |  | 
|  | // Clears the current keyword. | 
|  | void ClearKeyword(); | 
|  |  | 
|  | // Clears additional text. | 
|  | void ClearAdditionalText(); | 
|  |  | 
|  | // Called when the view is gaining focus.  |control_down| is whether the | 
|  | // control key is down (at the time we're gaining focus). | 
|  | void OnSetFocus(bool control_down); | 
|  |  | 
|  | // Starts a request for zero-prefix suggestions if no query is currently | 
|  | // running and the popup is closed. This can be called multiple times without | 
|  | // harm, since it will early-exit if an earlier request is in progress or | 
|  | // done. | 
|  | void StartZeroSuggestRequest(bool user_clobbered_permanent_text = false); | 
|  |  | 
|  | // Sets the visibility of the caret in the omnibox, if it has focus. The | 
|  | // visibility of the caret is reset to visible if either | 
|  | //   - The user starts typing, or | 
|  | //   - We explicitly focus the omnibox again. | 
|  | // The latter case must be handled in three separate places--OnSetFocus(), | 
|  | // OmniboxView::SetFocus(), and the mouse handlers in OmniboxView. See | 
|  | // accompanying comments for why each of these is necessary. | 
|  | // | 
|  | // Caret visibility is tracked per-tab and updates automatically upon | 
|  | // switching tabs. | 
|  | void SetCaretVisibility(bool visible); | 
|  |  | 
|  | // If the ctrl key is down, marks it as consumed to prevent it from triggering | 
|  | // ctrl-enter behavior unless it is released and re-pressed. | 
|  | void ConsumeCtrlKey(); | 
|  |  | 
|  | // Sent before |OnKillFocus| and before the popup is closed. | 
|  | void OnWillKillFocus(); | 
|  |  | 
|  | // Called when the view is losing focus.  Resets some state. | 
|  | void OnKillFocus(); | 
|  |  | 
|  | // Called when the user presses the escape key.  Decides what, if anything, to | 
|  | // revert about any current edits.  Returns whether the key was handled. | 
|  | bool OnEscapeKeyPressed(); | 
|  |  | 
|  | // Called when the user presses or releases the control key.  Changes state as | 
|  | // necessary. | 
|  | void OnControlKeyChanged(bool pressed); | 
|  |  | 
|  | // Called when the user pastes in text. | 
|  | void OnPaste(); | 
|  |  | 
|  | // Called when the user presses arrow up, arrow down, page up, or page down. | 
|  | void OnUpOrDownPressed(bool down, bool page); | 
|  |  | 
|  | // Called when the user presses tab or shift+tab. The latter will traverse up | 
|  | // the selections instead of down. | 
|  | void OnTabPressed(bool shift); | 
|  |  | 
|  | // Called when the user presses the space key without modifiers. | 
|  | // Returns true if the space is handled in a special way, for example | 
|  | // entering keyword mode on a match somewhere down the list. | 
|  | bool OnSpacePressed(); | 
|  |  | 
|  | // Called when any relevant data changes.  This rolls together several | 
|  | // separate pieces of data into one call so we can update all the UI | 
|  | // efficiently. Specifically, it's invoked for temporary text, autocompletion, | 
|  | // and keyword changes. | 
|  | //   `temporary_text` is the new temporary text from the user selecting a | 
|  | //     different match. This will be empty when selecting a suggestion | 
|  | //     without a `fill_into_edit` (e.g. FOCUSED_BUTTON_HEADER) and when | 
|  | //     `is_temporary_test` is false. | 
|  | //   `is_temporary_text` is true if invoked because of a temporary text change | 
|  | //     or false if `temporary_text` should be ignored. | 
|  | //   `inline_autocompletion` is the autocompletion. | 
|  | //   `destination_for_temporary_text_change` is NULL (if temporary text should | 
|  | //     not change) or the pre-change destination URL (if temporary text should | 
|  | //     change) so we can save it off to restore later. | 
|  | //   `keyword` is the keyword to show a hint for if `is_keyword_hint` is true, | 
|  | //     or the currently selected keyword if `is_keyword_hint` is false (see | 
|  | //     comments on keyword_ and is_keyword_hint_). | 
|  | //   `additional_text` is additional omnibox text to be displayed adjacent to | 
|  | //     the omnibox view. | 
|  | //   `new_match` is the selected match when the user is changing selection, | 
|  | //     the default match if the user is typing, or an empty match when | 
|  | //     selecting a header. | 
|  | // Virtual to allow testing. | 
|  | virtual void OnPopupDataChanged(const std::u16string& temporary_text, | 
|  | bool is_temporary_text, | 
|  | const std::u16string& inline_autocompletion, | 
|  | const std::u16string& keyword, | 
|  | const std::u16string& keyword_placeholder, | 
|  | bool is_keyword_hint, | 
|  | const std::u16string& additional_text, | 
|  | const AutocompleteMatch& new_match); | 
|  |  | 
|  | // Called by the OmniboxView after something changes, with details about what | 
|  | // state changes occurred.  Updates internal state, updates the popup if | 
|  | // necessary, and returns true if any significant changes occurred.  Note that | 
|  | // |text_change.text_differs| may be set even if |text_change.old_text| == | 
|  | // |text_change.new_text|, e.g. if we've just committed an IME composition. | 
|  | // | 
|  | // If |allow_keyword_ui_change| is false then the change should not affect | 
|  | // keyword ui state, even if the text matches a keyword exactly. This value | 
|  | // may be false when the user is composing a text with an IME. | 
|  | bool OnAfterPossibleChange(const OmniboxView::StateChanges& state_changes, | 
|  | bool allow_keyword_ui_change); | 
|  |  | 
|  | // Called when the current match has changed in the OmniboxController. | 
|  | void OnCurrentMatchChanged(); | 
|  |  | 
|  | AutocompleteInput GetInputForTesting() const { return input_; } | 
|  |  | 
|  | // Name of the histogram tracking cut or copy omnibox commands. | 
|  | static const char kCutOrCopyAllTextHistogram[]; | 
|  |  | 
|  | // Just forwards the call to the OmniboxView referred within. | 
|  | void SetAccessibilityLabel(const AutocompleteMatch& match); | 
|  |  | 
|  | // Reverts the edit box from a temporary text back to the original user text. | 
|  | // Also resets the popup to the initial state. | 
|  | void RevertTemporaryTextAndPopup(); | 
|  |  | 
|  | // Returns true if the destination URL of the match is bookmarked. | 
|  | bool IsStarredMatch(const AutocompleteMatch& match) const; | 
|  |  | 
|  | // Gets the icon for the given `match`. | 
|  | gfx::Image GetMatchIcon(const AutocompleteMatch& match, | 
|  | SkColor vector_icon_color, | 
|  | bool dark_mode = false) const; | 
|  | // Gets the icon for the given `match` if the match was provided by an omnibox | 
|  | // API extension, otherwise returns empty image. | 
|  | gfx::Image GetMatchIconIfExtension(const AutocompleteMatch& match) const; | 
|  |  | 
|  | // Gets the suggestion group header text associated with the given suggestion | 
|  | // group ID. | 
|  | // In addition to calling `AutocompleteResult::GetHeaderForSuggestionGroup()`, | 
|  | // this function takes into account certain header visibility criteria (e.g. | 
|  | // experiment flags) to determine the proper header text, which will then be | 
|  | // used by the relevant code to conditionally show suggestion group headers | 
|  | // in the Omnibox/Realbox popup. | 
|  | std::u16string GetSuggestionGroupHeaderText( | 
|  | const std::optional<omnibox::GroupId>& suggestion_group_id) const; | 
|  |  | 
|  | // Returns true if the popup exists and is open. Virtual for testing. | 
|  | virtual bool PopupIsOpen() const; | 
|  |  | 
|  | // Called when the user hits escape after arrowing around the popup.  This | 
|  | // will reset the popup to the initial state. | 
|  | void ResetPopupToInitialState(); | 
|  |  | 
|  | // Gets popup's current selection. | 
|  | OmniboxPopupSelection GetPopupSelection() const; | 
|  |  | 
|  | // Sets the current popup selection to |new_selection|. Caller is responsible | 
|  | // for making sure |new_selection| is valid. This assumes the popup is open. | 
|  | // This will update all state and repaint the necessary parts of the window, | 
|  | // as well as updating the textfield with the new temporary text. | 
|  | // |reset_to_default| restores the original inline autocompletion. | 
|  | // |force_update_ui| updates the UI even if the selection has not changed. | 
|  | void SetPopupSelection(OmniboxPopupSelection new_selection, | 
|  | bool reset_to_default = false, | 
|  | bool force_update_ui = false); | 
|  |  | 
|  | // Returns true if popup selection is on the initial line, which is usually | 
|  | // the default match (except in the no-default-match case). | 
|  | bool IsPopupSelectionOnInitialLine() const; | 
|  |  | 
|  | // Returns true if the control represented by |selection.state| is present on | 
|  | // the match in |selection.line|. This is the source-of-truth the UI code | 
|  | // should query to decide whether or not to draw the control. | 
|  | bool IsPopupControlPresentOnMatch(OmniboxPopupSelection selection) const; | 
|  |  | 
|  | // From popup, tries to erase the suggestion at |line|. This should determine | 
|  | // if the item at |line| can be removed from history, and if so, remove it | 
|  | // and update the popup. | 
|  | void TryDeletingPopupLine(size_t line); | 
|  |  | 
|  | // Returns the popup's accessibility label for current selection. This is an | 
|  | // extended version of AutocompleteMatchType::ToAccessibilityLabel() which | 
|  | // also returns narration about the any focused secondary button. | 
|  | // Never call this when the current selection is kNoMatch. | 
|  | std::u16string GetPopupAccessibilityLabelForCurrentSelection( | 
|  | const std::u16string& match_text, | 
|  | bool include_positional_info, | 
|  | int* label_prefix_length = nullptr); | 
|  |  | 
|  | std::u16string GetPopupAccessibilityLabelForAimButton(); | 
|  |  | 
|  | // The IPH message that sometimes appears at the bottom of the Omnibox is | 
|  | // informational only and cannot be selected/focused. Its a11y label therefore | 
|  | // has to be read at the end of the last suggestion.  Returns the label for | 
|  | // the IPH row if the current selection is the one right before the IPH row. | 
|  | // Otherwise, returns an empty string. | 
|  | std::u16string MaybeGetPopupAccessibilityLabelForIPHSuggestion(); | 
|  |  | 
|  | // Invoked any time the result set of the controller changes. | 
|  | // TODO(orinj): This method seems like a good candidate for removal; it is | 
|  | // preserved here only to prevent possible behavior change while refactoring. | 
|  | void OnPopupResultChanged(); | 
|  |  | 
|  | // Lookup the bitmap for |result_index|. Returns nullptr if not found. | 
|  | const SkBitmap* GetPopupRichSuggestionBitmap(int result_index) const; | 
|  |  | 
|  | // Lookup the bitmap based on the image URL.  Similar to above, | 
|  | // but used to fetch bitmap where the `result_index` is unknown and where | 
|  | // there are possibly multiple suggestions with the same `keyword` but not the | 
|  | // `image_url` being looked up. Returns nullptr if not found. | 
|  | const SkBitmap* GetPopupRichSuggestionBitmap(const GURL& image_url) const; | 
|  |  | 
|  | // Lookup the icon bitmap based on the icon URL. Returns nullptr | 
|  | // if not found. | 
|  | const SkBitmap* GetIconBitmap(const GURL& icon_url) const; | 
|  |  | 
|  | // Stores the image in a local data member and schedules a repaint. | 
|  | void SetPopupRichSuggestionBitmap(int result_index, const SkBitmap& bitmap); | 
|  |  | 
|  | // Stores the icon in a local data member and schedules a repaint. | 
|  | void SetIconBitmap(const GURL& icon_url, const SkBitmap& bitmap); | 
|  |  | 
|  | void SetAutocompleteInput(AutocompleteInput input); | 
|  |  | 
|  | // Called to indicate a navigation may occur based on | 
|  | // |navigation_predictor| to the suggestion on |line|. | 
|  | void OnNavigationLikely( | 
|  | size_t line, | 
|  | omnibox::mojom::NavigationPredictor navigation_predictor); | 
|  |  | 
|  | protected: | 
|  | // Utility method to get current PrefService; protected instead of private | 
|  | // because it may be overridden by derived test classes. | 
|  | virtual PrefService* GetPrefService(); | 
|  | virtual const PrefService* GetPrefService() const; | 
|  |  | 
|  | private: | 
|  | friend class OmniboxControllerTest; | 
|  | friend class TestOmniboxEditModel; | 
|  | FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKey); | 
|  | FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKeyOnRequestFocus); | 
|  | FRIEND_TEST_ALL_PREFIXES(OmniboxEditModelTest, ConsumeCtrlKeyOnCtrlAction); | 
|  | FRIEND_TEST_ALL_PREFIXES( | 
|  | OmniboxEditModelPopupTest, | 
|  | GetPopupRichSuggestionBitmapForMatchWithoutAssociatedKeyword); | 
|  | FRIEND_TEST_ALL_PREFIXES( | 
|  | OmniboxEditModelPopupTest, | 
|  | GetPopupRichSuggestionBitmapForMatchWithAssociatedKeyword); | 
|  |  | 
|  | enum class PasteState { | 
|  | kNone,     // Most recent edit was not a paste. | 
|  | kPasting,  // In the middle of doing a paste. We need this intermediate | 
|  | // state because `OnPaste()` does the actual detection of paste, | 
|  | // but `OnAfterPossibleChange()` has to update the paste state | 
|  | // for every edit. If `OnPaste()` set the state directly to | 
|  | // kPasted, `OnAfterPossibleChange()` wouldn't know whether that | 
|  | // represented the current edit or a past one. | 
|  | kPasted,   // Most recent edit was a paste. | 
|  | }; | 
|  |  | 
|  | enum class ControlKeyState { | 
|  | kUp,              // The control key is not depressed. | 
|  | kDown,            // The control key is depressed and should trigger the | 
|  | // "ctrl-enter" behavior when the user hits enter. | 
|  | kDownAndConsumed  // The control key is depressed, but has been consumed | 
|  | // and should not trigger the "ctrl-enter" behavior. | 
|  | // The control key becomes consumed if it has been used | 
|  | // for another action such as focusing the location bar | 
|  | // with ctrl-l or copying the selected text with ctrl-c. | 
|  | }; | 
|  |  | 
|  | AutocompleteController* autocomplete_controller() const; | 
|  |  | 
|  | // If no query is in progress, starts working on an autocomplete query. | 
|  | // Returns true if started; false otherwise. | 
|  | bool MaybeStartQueryForPopup(); | 
|  |  | 
|  | // Changes the popup selection to the next available selection. Stepping the | 
|  | // popup selection gives special consideration for keyword mode state. | 
|  | void StepPopupSelection(OmniboxPopupSelection::Direction direction, | 
|  | OmniboxPopupSelection::Step step); | 
|  |  | 
|  | // Asks the browser to load the popup's currently selected item, using the | 
|  | // supplied disposition.  This may close the popup. | 
|  | void AcceptInput( | 
|  | WindowOpenDisposition disposition, | 
|  | base::TimeTicks match_selection_timestamp = base::TimeTicks()); | 
|  |  | 
|  | // Asks the browser to load |match| or execute one of its actions | 
|  | // according to |selection|. | 
|  | // | 
|  | // OpenMatch() needs to know the original text that drove this action.  If | 
|  | // |pasted_text| is non-empty, this is a Paste-And-Go/Search action, and | 
|  | // that's the relevant input text.  Otherwise, the relevant input text is | 
|  | // either the user text or the display URL, depending on if user input is | 
|  | // in progress. | 
|  | // | 
|  | // |match| is passed by value for two reasons: | 
|  | // (1) This function needs to modify |match|, so a const ref isn't | 
|  | //     appropriate.  Callers don't actually care about the modifications, so a | 
|  | //     pointer isn't required. | 
|  | // (2) The passed-in match is, on the caller side, typically coming from data | 
|  | //     associated with the popup.  Since this call can close the popup, that | 
|  | //     could clear that data, leaving us with a pointer-to-garbage.  So at | 
|  | //     some point someone needs to make a copy of the match anyway, to | 
|  | //     preserve it past the popup closure. | 
|  | void OpenMatch(OmniboxPopupSelection selection, | 
|  | AutocompleteMatch match, | 
|  | WindowOpenDisposition disposition, | 
|  | const GURL& alternate_nav_url, | 
|  | const std::u16string& pasted_text, | 
|  | base::TimeTicks match_selection_timestamp = base::TimeTicks()); | 
|  |  | 
|  | // Updates the feedback type on the match at the given index and schedules a | 
|  | // repaint to update the suggestion view. On negative feedback, also shows the | 
|  | // feedback form. | 
|  | void UpdateFeedbackOnMatch(size_t match_index, FeedbackType feedback_type); | 
|  |  | 
|  | // An internal method to set the user text. Notably, this differs from | 
|  | // SetUserText because it does not change the user-input-in-progress state. | 
|  | void InternalSetUserText(const std::u16string& text); | 
|  |  | 
|  | // Conversion between user text and display text. User text is the text the | 
|  | // user has input. Display text is the text being shown in the edit. The | 
|  | // two are different if a keyword is selected. | 
|  | std::u16string MaybeStripKeyword(const std::u16string& text) const; | 
|  | std::u16string MaybePrependKeyword(const std::u16string& text) const; | 
|  |  | 
|  | // Copies a match corresponding to the current text into |match|, and | 
|  | // populates |alternate_nav_url| as well if it's not nullptr. If the popup | 
|  | // is closed, the match is generated from the autocomplete classifier. | 
|  | void GetInfoForCurrentText(AutocompleteMatch* match, | 
|  | GURL* alternate_nav_url) const; | 
|  |  | 
|  | // Checks whether keyword mode space-triggering has been disabled either by | 
|  | // a pref or relevant feature flags. If the setting isn't available on the | 
|  | // search engines page, the pref should be ignored.  Returns true if space | 
|  | // triggering is enabled, false otherwise. | 
|  | bool AllowKeywordSpaceTriggering() const; | 
|  |  | 
|  | // Accepts current keyword if the user just typed a space at the end of | 
|  | // |new_text|.  This handles both of the following cases: | 
|  | //   (assume "foo" is a keyword, | is the input caret, [] is selected text) | 
|  | //   foo| -> foo |      (a space was appended to a keyword) | 
|  | //   foo[bar] -> foo |  (a space replaced other text after a keyword) | 
|  | // Returns true if the current keyword is accepted. | 
|  | bool MaybeAcceptKeywordBySpace(const std::u16string& new_text); | 
|  |  | 
|  | // Checks whether the user inserted a space into |old_text| and by doing so | 
|  | // created a |new_text| that looks like "<keyword> <search phrase>". | 
|  | bool CreatedKeywordSearchByInsertingSpaceInMiddle( | 
|  | const std::u16string& old_text, | 
|  | const std::u16string& new_text, | 
|  | size_t caret_position) const; | 
|  |  | 
|  | // Checks if a given character is a valid space character for accepting | 
|  | // keyword. | 
|  | static bool IsSpaceCharForAcceptingKeyword(wchar_t c); | 
|  |  | 
|  | // Sets the state of user_input_in_progress_. Returns whether said state | 
|  | // changed, so that the caller can evoke NotifyObserversInputInProgress(). | 
|  | bool SetInputInProgressNoNotify(bool in_progress); | 
|  |  | 
|  | // Notifies the observers that the state has changed. | 
|  | void NotifyObserversInputInProgress(bool in_progress); | 
|  |  | 
|  | // If focus_state_ does not match |state|, we update it and notify the | 
|  | // InstantController about the change (passing along the |reason| for the | 
|  | // change). If the caret visibility changes, we call ApplyCaretVisibility() on | 
|  | // the view. | 
|  | void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason); | 
|  |  | 
|  | // This is an event handler that notifies the popup view of match icon | 
|  | // changes. | 
|  | void OnFaviconFetched(const GURL& page_url, const gfx::Image& icon) const; | 
|  |  | 
|  | // Returns view text if there is a view. Until the model is made the | 
|  | // primary data source, this should not be called when there's no view. | 
|  | std::u16string GetText() const; | 
|  |  | 
|  | // Always use these to set keyword members instead of mutating them directly. | 
|  | void SetKeyword(const std::u16string& keyword); | 
|  | void SetKeywordPlaceholder(const std::u16string& keyword_placeholder); | 
|  |  | 
|  | // Record various UMA metrics associated with the AIM page action. | 
|  | // `query_text` represents the text entered by the user at activation time. | 
|  | // `activated` represents whether or not the user activated the page action. | 
|  | // `via_keyboard` represents the page action entry method (i.e. `true` = | 
|  | // keyboard event / `false` = mouse/gesture event). | 
|  | void RecordAiModeMetrics(const std::u16string& query_text, | 
|  | bool activated, | 
|  | bool via_keyboard); | 
|  |  | 
|  | // Owns this. | 
|  | const raw_ptr<OmniboxController> controller_; | 
|  |  | 
|  | // Owns `OmniboxController` which owns this. | 
|  | const raw_ptr<OmniboxView> view_; | 
|  |  | 
|  | OmniboxFocusState focus_state_ = OMNIBOX_FOCUS_NONE; | 
|  |  | 
|  | // Display-only text representing the current page. This could either: | 
|  | //  - The same as |url_for_editing_| if Steady State Elisions is OFF. | 
|  | //  - A simplified version of |url_for_editing_| with some destructive | 
|  | //    elisions applied. This is the case if Steady State Elisions is ON. | 
|  | // | 
|  | // This should not be considered suitable for editing. | 
|  | std::u16string display_text_; | 
|  |  | 
|  | // The initial text representing the current URL suitable for editing. | 
|  | // This should fully represent the current URL without any meaning-changing | 
|  | // elisions applied - and is suitable for user editing. | 
|  | std::u16string url_for_editing_; | 
|  |  | 
|  | // This flag is true when the user has modified the contents of the edit, but | 
|  | // not yet accepted them.  We use this to determine when we need to save | 
|  | // state (on switching tabs) and whether changes to the page URL should be | 
|  | // immediately displayed. | 
|  | // This flag *should* be true in a superset of the cases where the popup is | 
|  | // open. Except (crbug.com/1340378) for zero suggestions when the popup was | 
|  | // opened with ctrl+L or a mouse click (as opposed to the down arrow). | 
|  | bool user_input_in_progress_ = false; | 
|  |  | 
|  | // The text that the user has entered.  This does not include inline | 
|  | // autocomplete text that has not yet been accepted.  |user_text_| can | 
|  | // contain a string without |user_input_in_progress_| being true. | 
|  | // For instance, this is the case when the user has unelided a URL without | 
|  | // modifying its contents. | 
|  | std::u16string user_text_; | 
|  |  | 
|  | // Used to know what should be displayed. Updated when e.g. the popup | 
|  | // selection changes, the results change, on navigation, on tab switch etc; it | 
|  | // should always be up-to-date. | 
|  | AutocompleteMatch current_match_; | 
|  |  | 
|  | // We keep track of when the user last focused on the omnibox. | 
|  | base::TimeTicks last_omnibox_focus_; | 
|  |  | 
|  | // Indicates whether the current interaction with the Omnibox resulted in | 
|  | // navigation (true), or user leaving the omnibox without taking any action | 
|  | // (false). | 
|  | // The value is initialized when the Omnibox receives focus and available for | 
|  | // use when the focus is about to be cleared. | 
|  | bool focus_resulted_in_navigation_ = false; | 
|  |  | 
|  | // We keep track of when the user began modifying the omnibox text. | 
|  | // This should be valid whenever user_input_in_progress_ is true. | 
|  | base::TimeTicks time_user_first_modified_omnibox_; | 
|  |  | 
|  | // Inline autocomplete is allowed if the user has not just deleted text, and | 
|  | // no temporary text is showing.  In this case, inline_autocompletion_ is | 
|  | // appended to the user_text_ and displayed selected (at least initially). | 
|  | // | 
|  | // NOTE: When the popup is closed there should never be inline autocomplete | 
|  | // text (actions that close the popup should either accept the text, convert | 
|  | // it to a normal selection, or change the edit entirely). | 
|  | bool just_deleted_text_ = false; | 
|  | std::u16string inline_autocompletion_; | 
|  |  | 
|  | // Used by OnPopupDataChanged to keep track of whether there is currently a | 
|  | // temporary text. | 
|  | // | 
|  | // Example of use: If the user types "goog", then arrows down in the | 
|  | // autocomplete popup until, say, "google.com" appears in the edit box, then | 
|  | // the user_text_ is still "goog", and "google.com" is "temporary text". | 
|  | // When the user hits <esc>, the edit box reverts to "goog".  Hit <esc> again | 
|  | // and the popup is closed and "goog" is replaced by the permanent display | 
|  | // URL, which is the URL of the current page. | 
|  | // | 
|  | // original_user_text_with_keyword_ tracks the user_text_ before keywords are | 
|  | // removed. When accepting a keyword (from either a default match or another | 
|  | // lower in the dropdown), the user_text_ is destructively trimmed of its 1st | 
|  | // word. In order to restore the user_text_ properly when the omnibox reverts, | 
|  | // e.g. by pressing <escape> or pressing <up> until the first result is | 
|  | // selected, we track original_user_text_with_keyword_. | 
|  | // original_user_text_with_keyword_ is null if a keyword has not been | 
|  | // accepted. | 
|  | bool has_temporary_text_ = false; | 
|  | std::u16string original_user_text_with_keyword_; | 
|  |  | 
|  | // When the user's last action was to paste, we disallow inline autocomplete | 
|  | // (on the theory that the user is trying to paste in a new URL or part of | 
|  | // one, and in either case inline autocomplete would get in the way). | 
|  | PasteState paste_state_ = PasteState::kNone; | 
|  |  | 
|  | // Whether the control key is depressed.  We track this to avoid calling | 
|  | // UpdatePopup() repeatedly if the user holds down the key, and to know | 
|  | // whether to trigger "ctrl-enter" behavior. | 
|  | ControlKeyState control_key_state_ = ControlKeyState::kUp; | 
|  |  | 
|  | // The keyword associated with the current match.  The user may have an actual | 
|  | // selected keyword, or just some input text that looks like a keyword (so we | 
|  | // can show a hint to press <tab>).  This is the keyword in either case; | 
|  | // is_keyword_hint_ (below) distinguishes the two cases. | 
|  | std::u16string keyword_; | 
|  |  | 
|  | // The placeholder text displayed for the keyword the user has selected. | 
|  | // Usually empty. Only used when the user input is empty. | 
|  | std::u16string keyword_placeholder_; | 
|  |  | 
|  | // True if the keyword associated with this match is merely a hint, i.e. the | 
|  | // user hasn't actually selected a keyword yet.  When this is true, we can use | 
|  | // keyword_ to show a "Press <tab> to search" sort of hint. | 
|  | bool is_keyword_hint_ = false; | 
|  |  | 
|  | // Indicates how the user entered keyword mode if the user is actually in | 
|  | // keyword mode.  Otherwise, the value of this variable is INVALID.  This | 
|  | // is used to restore the user's search terms upon a call to ClearKeyword(). | 
|  | metrics::OmniboxEventProto::KeywordModeEntryMethod | 
|  | keyword_mode_entry_method_ = metrics::OmniboxEventProto::INVALID; | 
|  |  | 
|  | // This is needed to properly update the SearchModel state when the user | 
|  | // presses escape. | 
|  | bool in_revert_ = false; | 
|  |  | 
|  | // Indicates if the upcoming autocomplete search is allowed to be treated as | 
|  | // an exact keyword match.  If this is true then keyword mode will be | 
|  | // triggered automatically if the input is "<keyword> <search string>".  We | 
|  | // allow this when CreatedKeywordSearchByInsertingSpaceInMiddle() is true. | 
|  | // This has no effect if we're already in keyword mode. | 
|  | bool allow_exact_keyword_match_ = false; | 
|  |  | 
|  | // The input that was sent to the AutocompleteController. Since no | 
|  | // autocomplete query is started after a tab switch, it is possible for this | 
|  | // |input_| to differ from the one currently stored in AutocompleteController. | 
|  | AutocompleteInput input_; | 
|  |  | 
|  | // Rich suggestion bitmaps for popup keyed by `result_index`. These are | 
|  | // cleared when `OmniboxPopupViewViews` is initialized and destroyed, and on | 
|  | // `OnPopupResultChanged()`. | 
|  | std::map<int, SkBitmap> rich_suggestion_bitmaps_; | 
|  |  | 
|  | // Icon bitmaps for popup keyed by `icon_url`. These are cleared when | 
|  | // `OmniboxPopupViewViews` is initialized and destroyed. This differs from | 
|  | // `rich_suggestion_bitmaps_` since they are not cleared on | 
|  | // `OnPopupResultChanged()`, which allows for fetching the icon even when the | 
|  | // popup is closed. | 
|  | std::map<GURL, SkBitmap> icon_bitmaps_; | 
|  |  | 
|  | // The popup view is nullptr when there's no popup, and is non-null when | 
|  | // a popup view exists (i.e. between calls to `set_popup_view`). | 
|  | raw_ptr<OmniboxPopupView> popup_view_ = nullptr; | 
|  |  | 
|  | // The current popup selection; set to normal kNoMatch when there's no popup. | 
|  | OmniboxPopupSelection popup_selection_ = | 
|  | OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch, | 
|  | OmniboxPopupSelection::NORMAL); | 
|  |  | 
|  | // When a result changes, this informs of the URL in the previously selected | 
|  | // suggestion whose tab switch button was focused, so that we may compare | 
|  | // if equal. | 
|  | GURL old_focused_url_; | 
|  |  | 
|  | // See comment on `Observer`. | 
|  | mutable base::ObserverList<Observer> observers_; | 
|  |  | 
|  | base::WeakPtrFactory<OmniboxEditModel> weak_factory_{this}; | 
|  | }; | 
|  |  | 
|  | #endif  // CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_EDIT_MODEL_H_ |