1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_
6#define COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_
7
8#include <stddef.h>
9
10#include <map>
11
12#include "base/basictypes.h"
13#include "components/metrics/proto/omnibox_event.pb.h"
14#include "components/omnibox/autocomplete_match.h"
15#include "url/gurl.h"
16
17class AutocompleteInput;
18class AutocompleteProvider;
19class TemplateURLService;
20
21// All matches from all providers for a particular query.  This also tracks
22// what the default match should be if the user doesn't manually select another
23// match.
24class AutocompleteResult {
25 public:
26  typedef ACMatches::const_iterator const_iterator;
27  typedef ACMatches::iterator iterator;
28
29  // The "Selection" struct is the information we need to select the same match
30  // in one result set that was selected in another.
31  struct Selection {
32    Selection()
33        : provider_affinity(NULL),
34          is_history_what_you_typed_match(false) {
35    }
36
37    // Clear the selection entirely.
38    void Clear();
39
40    // True when the selection is empty.
41    bool empty() const {
42      return destination_url.is_empty() && !provider_affinity &&
43          !is_history_what_you_typed_match;
44    }
45
46    // The desired destination URL.
47    GURL destination_url;
48
49    // The desired provider.  If we can't find a match with the specified
50    // |destination_url|, we'll use the best match from this provider.
51    const AutocompleteProvider* provider_affinity;
52
53    // True when this is the HistoryURLProvider's "what you typed" match.  This
54    // can't be tracked using |destination_url| because its URL changes on every
55    // keystroke, so if this is set, we'll preserve the selection by simply
56    // choosing the new "what you typed" entry and ignoring |destination_url|.
57    bool is_history_what_you_typed_match;
58  };
59
60  // Max number of matches we'll show from the various providers.
61  static const size_t kMaxMatches;
62
63  AutocompleteResult();
64  ~AutocompleteResult();
65
66  // Copies matches from |old_matches| to provide a consistant result set. See
67  // comments in code for specifics.
68  void CopyOldMatches(const AutocompleteInput& input,
69                      const AutocompleteResult& old_matches,
70                      TemplateURLService* template_url_service);
71
72  // Adds a new set of matches to the result set.  Does not re-sort.
73  void AppendMatches(const ACMatches& matches);
74
75  // Removes duplicates, puts the list in sorted order and culls to leave only
76  // the best kMaxMatches matches.  Sets the default match to the best match
77  // and updates the alternate nav URL.
78  void SortAndCull(const AutocompleteInput& input,
79                   TemplateURLService* template_url_service);
80
81  // Returns true if at least one match was copied from the last result.
82  bool HasCopiedMatches() const;
83
84  // Vector-style accessors/operators.
85  size_t size() const;
86  bool empty() const;
87  const_iterator begin() const;
88  iterator begin();
89  const_iterator end() const;
90  iterator end();
91
92  // Returns the match at the given index.
93  const AutocompleteMatch& match_at(size_t index) const;
94  AutocompleteMatch* match_at(size_t index);
95
96  // Get the default match for the query (not necessarily the first).  Returns
97  // end() if there is no default match.
98  const_iterator default_match() const { return default_match_; }
99
100  // Returns true if the top match is a verbatim search or URL match (see
101  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
102  // some kind of verbatim match.  In this case, the top match will be hidden,
103  // and nothing in the dropdown will appear selected by default; hitting enter
104  // will navigate to the (hidden) default match, while pressing the down arrow
105  // key will select the first visible match, which is actually the second match
106  // in the result set.
107  //
108  // Hiding the top match in these cases is possible because users should
109  // already know what will happen on hitting enter from the omnibox text
110  // itself, without needing to see the same text appear again, selected, just
111  // below their typing.  Instead, by hiding the verbatim match, there is one
112  // less line to skip over in order to visually scan downwards to see other
113  // suggested matches.  This makes it more likely that users will see and
114  // select useful non-verbatim matches.  (Note that hiding the verbatim match
115  // this way is similar to how most other browsers' address bars behave.)
116  //
117  // We avoid hiding when the top two matches are both verbatim in order to
118  // avoid potential confusion if a user were to see the second match just below
119  // their typing and assume it would be the default action.
120  //
121  // Note that if the top match should be hidden and it is the only match,
122  // the dropdown should be closed.
123  bool ShouldHideTopMatch() const;
124
125  // Returns true if the top match is a verbatim search or URL match (see
126  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
127  // some kind of verbatim match.
128  bool TopMatchIsStandaloneVerbatimMatch() const;
129
130  const GURL& alternate_nav_url() const { return alternate_nav_url_; }
131
132  // Clears the matches for this result set.
133  void Reset();
134
135  void Swap(AutocompleteResult* other);
136
137#ifndef NDEBUG
138  // Does a data integrity check on this result.
139  void Validate() const;
140#endif
141
142  // Compute the "alternate navigation URL" for a given match. This is obtained
143  // by interpreting the user input directly as a URL. See comments on
144  // |alternate_nav_url_|.
145  static GURL ComputeAlternateNavUrl(const AutocompleteInput& input,
146                                     const AutocompleteMatch& match);
147
148  // Sort |matches| by destination, taking into account demotions based on
149  // |page_classification| when resolving ties about which of several
150  // duplicates to keep.  The matches are also deduplicated.  If
151  // |set_duplicate_matches| is true, the duplicate matches are stored in the
152  // |duplicate_matches| vector of the corresponding AutocompleteMatch.
153  static void DedupMatchesByDestination(
154      metrics::OmniboxEventProto::PageClassification page_classification,
155      bool set_duplicate_matches,
156      ACMatches* matches);
157
158 private:
159  friend class AutocompleteProviderTest;
160
161  typedef std::map<AutocompleteProvider*, ACMatches> ProviderToMatches;
162
163#if defined(OS_ANDROID)
164  // iterator::difference_type is not defined in the STL that we compile with on
165  // Android.
166  typedef int matches_difference_type;
167#else
168  typedef ACMatches::iterator::difference_type matches_difference_type;
169#endif
170
171  // Returns true if |matches| contains a match with the same destination as
172  // |match|.
173  static bool HasMatchByDestination(const AutocompleteMatch& match,
174                                    const ACMatches& matches);
175
176  // operator=() by another name.
177  void CopyFrom(const AutocompleteResult& rhs);
178
179  // Populates |provider_to_matches| from |matches_|.
180  void BuildProviderToMatches(ProviderToMatches* provider_to_matches) const;
181
182  // Copies matches into this result. |old_matches| gives the matches from the
183  // last result, and |new_matches| the results from this result.
184  void MergeMatchesByProvider(
185      metrics::OmniboxEventProto::PageClassification page_classification,
186      const ACMatches& old_matches,
187      const ACMatches& new_matches);
188
189  ACMatches matches_;
190
191  const_iterator default_match_;
192
193  // The "alternate navigation URL", if any, for this result set.  This is a URL
194  // to try offering as a navigational option in case the user navigated to the
195  // URL of the default match but intended something else.  For example, if the
196  // user's local intranet contains site "foo", and the user types "foo", we
197  // default to searching for "foo" when the user may have meant to navigate
198  // there.  In cases like this, the default match will point to the "search for
199  // 'foo'" result, and this will contain "http://foo/".
200  GURL alternate_nav_url_;
201
202  DISALLOW_COPY_AND_ASSIGN(AutocompleteResult);
203};
204
205#endif  // COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_
206