1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_
6#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_
7
8#include <stddef.h>
9
10#include <map>
11
12#include "base/basictypes.h"
13#include "chrome/browser/autocomplete/autocomplete_input.h"
14#include "chrome/browser/autocomplete/autocomplete_match.h"
15#include "url/gurl.h"
16
17class AutocompleteInput;
18class AutocompleteProvider;
19class Profile;
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  // The lowest score a match can have and still potentially become the default
64  // match for the result set.
65  static const int kLowestDefaultScore;
66
67  AutocompleteResult();
68  ~AutocompleteResult();
69
70  // Copies matches from |old_matches| to provide a consistant result set. See
71  // comments in code for specifics.
72  void CopyOldMatches(const AutocompleteInput& input,
73                      const AutocompleteResult& old_matches,
74                      Profile* profile);
75
76  // Adds a new set of matches to the result set.  Does not re-sort.
77  void AppendMatches(const ACMatches& matches);
78
79  // Removes duplicates, puts the list in sorted order and culls to leave only
80  // the best kMaxMatches matches.  Sets the default match to the best match
81  // and updates the alternate nav URL.
82  void SortAndCull(const AutocompleteInput& input, Profile* profile);
83
84  // Returns true if at least one match was copied from the last result.
85  bool HasCopiedMatches() const;
86
87  // Vector-style accessors/operators.
88  size_t size() const;
89  bool empty() const;
90  const_iterator begin() const;
91  iterator begin();
92  const_iterator end() const;
93  iterator end();
94
95  // Returns the match at the given index.
96  const AutocompleteMatch& match_at(size_t index) const;
97  AutocompleteMatch* match_at(size_t index);
98
99  // Get the default match for the query (not necessarily the first).  Returns
100  // end() if there is no default match.
101  const_iterator default_match() const { return default_match_; }
102
103  // Returns true if the top match is a verbatim search or URL match (see
104  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
105  // some kind of verbatim match.  In this case, the top match will be hidden,
106  // and nothing in the dropdown will appear selected by default; hitting enter
107  // will navigate to the (hidden) default match, while pressing the down arrow
108  // key will select the first visible match, which is actually the second match
109  // in the result set.
110  //
111  // Hiding the top match in these cases is possible because users should
112  // already know what will happen on hitting enter from the omnibox text
113  // itself, without needing to see the same text appear again, selected, just
114  // below their typing.  Instead, by hiding the verbatim match, there is one
115  // less line to skip over in order to visually scan downwards to see other
116  // suggested matches.  This makes it more likely that users will see and
117  // select useful non-verbatim matches.  (Note that hiding the verbatim match
118  // this way is similar to how most other browsers' address bars behave.)
119  //
120  // We avoid hiding when the top two matches are both verbatim in order to
121  // avoid potential confusion if a user were to see the second match just below
122  // their typing and assume it would be the default action.
123  //
124  // Note that if the top match should be hidden and it is the only match,
125  // the dropdown should be closed.
126  bool ShouldHideTopMatch() const;
127
128  // Returns true if the top match is a verbatim search or URL match (see
129  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
130  // some kind of verbatim match.
131  bool TopMatchIsVerbatimAndHasNoConsecutiveVerbatimMatches() const;
132
133  const GURL& alternate_nav_url() const { return alternate_nav_url_; }
134
135  // Clears the matches for this result set.
136  void Reset();
137
138  void Swap(AutocompleteResult* other);
139
140#ifndef NDEBUG
141  // Does a data integrity check on this result.
142  void Validate() const;
143#endif
144
145  // Compute the "alternate navigation URL" for a given match. This is obtained
146  // by interpreting the user input directly as a URL. See comments on
147  // |alternate_nav_url_|.
148  static GURL ComputeAlternateNavUrl(const AutocompleteInput& input,
149                                     const AutocompleteMatch& match);
150
151 private:
152  friend class AutocompleteProviderTest;
153
154  typedef std::map<AutocompleteProvider*, ACMatches> ProviderToMatches;
155
156#if defined(OS_ANDROID)
157  // iterator::difference_type is not defined in the STL that we compile with on
158  // Android.
159  typedef int matches_difference_type;
160#else
161  typedef ACMatches::iterator::difference_type matches_difference_type;
162#endif
163
164  // Returns true if |matches| contains a match with the same destination as
165  // |match|.
166  static bool HasMatchByDestination(const AutocompleteMatch& match,
167                                    const ACMatches& matches);
168
169  // operator=() by another name.
170  void CopyFrom(const AutocompleteResult& rhs);
171
172  // Adds a single match. The match is inserted at the appropriate position
173  // based on relevancy and display order. This is ONLY for use after
174  // SortAndCull() has been invoked, and preserves default_match_.
175  void AddMatch(AutocompleteInput::PageClassification page_classification,
176                const AutocompleteMatch& match);
177
178  // Populates |provider_to_matches| from |matches_|.
179  void BuildProviderToMatches(ProviderToMatches* provider_to_matches) const;
180
181  // Copies matches into this result. |old_matches| gives the matches from the
182  // last result, and |new_matches| the results from this result.
183  void MergeMatchesByProvider(
184      AutocompleteInput::PageClassification page_classification,
185      const ACMatches& old_matches,
186      const ACMatches& new_matches);
187
188  ACMatches matches_;
189
190  const_iterator default_match_;
191
192  // The "alternate navigation URL", if any, for this result set.  This is a URL
193  // to try offering as a navigational option in case the user navigated to the
194  // URL of the default match but intended something else.  For example, if the
195  // user's local intranet contains site "foo", and the user types "foo", we
196  // default to searching for "foo" when the user may have meant to navigate
197  // there.  In cases like this, the default match will point to the "search for
198  // 'foo'" result, and this will contain "http://foo/".
199  GURL alternate_nav_url_;
200
201  DISALLOW_COPY_AND_ASSIGN(AutocompleteResult);
202};
203
204#endif  // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_
205