autocomplete_popup_model.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "unicode/ubidi.h"
83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/autocomplete/autocomplete_edit.h"
11513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/browser/autocomplete/autocomplete_match.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/autocomplete/autocomplete_popup_view.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/profile.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extensions_service.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/search_engines/template_url.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/search_engines/template_url_model.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/notification_service.h"
183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "gfx/rect.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// AutocompletePopupModel
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompletePopupModel::AutocompletePopupModel(
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AutocompletePopupView* popup_view,
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AutocompleteEditModel* edit_model,
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Profile* profile)
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : view_(popup_view),
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      edit_model_(edit_model),
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      controller_(new AutocompleteController(profile)),
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_(profile),
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      hovered_line_(kNoMatch),
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      selected_line_(kNoMatch) {
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 Source<AutocompleteController>(controller_.get()));
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompletePopupModel::~AutocompletePopupModel() {
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::SetProfile(Profile* profile) {
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile);
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = profile;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller_->SetProfile(profile);
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::StartAutocomplete(
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::wstring& text,
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::wstring& desired_tld,
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool prevent_inline_autocomplete,
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool prefer_keyword) {
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The user is interacting with the edit, so stop tracking hover.
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetHoveredLine(kNoMatch);
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  manually_selected_match_.Clear();
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller_->Start(text, desired_tld, prevent_inline_autocomplete,
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     prefer_keyword, false);
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::StopAutocomplete() {
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller_->Stop(true);
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool AutocompletePopupModel::IsOpen() const {
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return view_->IsOpen();
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::SetHoveredLine(size_t line) {
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const bool is_disabling = (line == kNoMatch);
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(is_disabling || (line < controller_->result().size()));
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (line == hovered_line_)
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // Nothing to do
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the old hovered line is redrawn.  No need to redraw the selected
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // line since selection overrides hover so the appearance won't change.
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if ((hovered_line_ != kNoMatch) && (hovered_line_ != selected_line_))
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->InvalidateLine(hovered_line_);
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Change the hover to the new line.
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  hovered_line_ = line;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!is_disabling && (hovered_line_ != selected_line_))
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->InvalidateLine(hovered_line_);
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::SetSelectedLine(size_t line,
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             bool reset_to_default) {
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We should at least be dealing with the results of the current query.  Note
893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // that even if |line| was valid on entry, this may make it invalid.  We clamp
903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // it below.
913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  controller_->CommitIfQueryHasNeverBeenCommitted();
923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteResult& result = controller_->result();
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (result.empty())
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Cancel the query so the matches don't change on the user.
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller_->Stop(false);
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  line = std::min(line, result.size() - 1);
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteMatch& match = result.match_at(line);
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (reset_to_default) {
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manually_selected_match_.Clear();
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Track the user's selection until they cancel it.
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manually_selected_match_.destination_url = match.destination_url;
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manually_selected_match_.provider_affinity = match.provider;
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manually_selected_match_.is_history_what_you_typed_match =
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        match.is_history_what_you_typed_match;
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (line == selected_line_)
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // Nothing else to do.
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to update |selected_line_| before calling OnPopupDataChanged(), so
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // that when the edit notifies its controller that something has changed, the
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // controller can get the correct updated data.
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // NOTE: We should never reach here with no selected line; the same code that
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // opened the popup and made it possible to get here should have also set a
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selected line.
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CHECK(selected_line_ != kNoMatch);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GURL current_destination(result.match_at(selected_line_).destination_url);
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->InvalidateLine(selected_line_);
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  selected_line_ = line;
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->InvalidateLine(selected_line_);
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update the edit with the new data for this match.
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(pkasting): If |selected_line_| moves to the controller, this can be
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // eliminated and just become a call to the observer on the edit.
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::wstring keyword;
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (reset_to_default) {
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::wstring inline_autocomplete_text;
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if ((match.inline_autocomplete_offset != std::wstring::npos) &&
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (match.inline_autocomplete_offset < match.fill_into_edit.length())) {
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      inline_autocomplete_text =
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          match.fill_into_edit.substr(match.inline_autocomplete_offset);
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    edit_model_->OnPopupDataChanged(inline_autocomplete_text, NULL,
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    keyword, is_keyword_hint);
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    edit_model_->OnPopupDataChanged(match.fill_into_edit, &current_destination,
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    keyword, is_keyword_hint);
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Repaint old and new selected lines immediately, so that the edit doesn't
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // appear to update [much] faster than the popup.
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->PaintUpdatesNow();
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::ResetToDefaultMatch() {
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteResult& result = controller_->result();
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CHECK(!result.empty());
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetSelectedLine(result.default_match() - result.begin(), true);
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->OnDragCanceled();
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::InfoForCurrentSelection(
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AutocompleteMatch* match,
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GURL* alternate_nav_url) const {
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(match != NULL);
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteResult* result;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!controller_->done()) {
1653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // NOTE: Using latest_result() is important here since not only could it
1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // contain newer results than result() for the current query, it could even
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // refer to an entirely different query (e.g. if the user is typing rapidly
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // and the controller is purposefully delaying updates to avoid flicker).
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    result = &controller_->latest_result();
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // It's technically possible for |result| to be empty if no provider returns
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // a synchronous result but the query has not completed synchronously;
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // pratically, however, that should never actually happen.
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (result->empty())
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The user cannot have manually selected a match, or the query would have
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // stopped.  So the default match must be the desired selection.
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *match = *result->default_match();
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CHECK(IsOpen());
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The query isn't running, so the standard result set can't possibly be out
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // of date.
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    //
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // NOTE: In practice, it should actually be safe to use
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // controller_->latest_result() here too, since the controller keeps that
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // up-to-date.  However we generally try to avoid referring to that.
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    result = &controller_->result();
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If there are no results, the popup should be closed (so we should have
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // failed the CHECK above), and URLsForDefaultMatch() should have been
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // called instead.
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CHECK(!result->empty());
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CHECK(selected_line_ < result->size());
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *match = result->match_at(selected_line_);
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (alternate_nav_url && manually_selected_match_.empty())
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *alternate_nav_url = result->alternate_nav_url();
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match,
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                std::wstring* keyword) const {
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Assume we have no keyword until we find otherwise.
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  keyword->clear();
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the current match is a keyword, return that as the selected keyword.
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (TemplateURL::SupportsReplacement(match.template_url)) {
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    keyword->assign(match.template_url->keyword());
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See if the current match's fill_into_edit corresponds to a keyword.
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!profile_->GetTemplateURLModel())
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_->GetTemplateURLModel()->Load();
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const std::wstring keyword_hint(
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit));
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (keyword_hint.empty())
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't provide a hint if this keyword doesn't support replacement.
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const TemplateURL* const template_url =
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint);
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!TemplateURL::SupportsReplacement(template_url))
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
224513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // Don't provide a hint if this is an extension keyword not enabled for
225513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // incognito mode (and if this is an incognito profile).
226513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (template_url->IsExtensionKeyword() && profile_->IsOffTheRecord()) {
227513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const Extension* extension = profile_->GetExtensionsService()->
228513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch        GetExtensionById(template_url->GetExtensionId(), false);
229513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if (!profile_->GetExtensionsService()->IsIncognitoEnabled(extension))
230513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      return false;
231513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
232513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  keyword->assign(keyword_hint);
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochAutocompleteLog* AutocompletePopupModel::GetAutocompleteLog() {
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return new AutocompleteLog(controller_->input().text(),
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      controller_->input().type(), selected_line_, 0, controller_->result());
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::Move(int count) {
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteResult& result = controller_->result();
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (result.empty())
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The user is using the keyboard to change the selection, so stop tracking
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // hover.
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetHoveredLine(kNoMatch);
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Clamp the new line to [0, result_.count() - 1].
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const size_t new_line = selected_line_ + count;
2533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line,
2543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                  false);
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::TryDeletingCurrentItem() {
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We could use InfoForCurrentSelection() here, but it seems better to try
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and shift-delete the actual selection, rather than any "in progress, not
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // yet visible" one.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (selected_line_ == kNoMatch)
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Cancel the query so the matches don't change on the user.
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller_->Stop(false);
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteMatch& match =
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      controller_->result().match_at(selected_line_);
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (match.deletable) {
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const size_t selected_line = selected_line_;
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    controller_->DeleteMatch(match);  // This may synchronously notify us that
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      // the results have changed.
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const AutocompleteResult& result = controller_->result();
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!result.empty()) {
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Move the selection to the next choice after the deleted one.
2763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      // SetSelectedLine() will clamp to take care of the case where we deleted
2773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      // the last item.
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // TODO(pkasting): Eventually the controller should take care of this
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // before notifying us, reducing flicker.  At that point the check for
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // deletability can move there too.
2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      SetSelectedLine(selected_line, false);
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid AutocompletePopupModel::Observe(NotificationType type,
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     const NotificationSource& source,
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     const NotificationDetails& details) {
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_EQ(NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            type.value);
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const AutocompleteResult* result =
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Details<const AutocompleteResult>(details).ptr();
2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  selected_line_ = result->default_match() == result->end() ?
2953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      kNoMatch : static_cast<size_t>(result->default_match() - result->begin());
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // There had better not be a nonempty result set with no default match.
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CHECK((selected_line_ != kNoMatch) || result->empty());
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we're going to trim the window size to no longer include the hovered
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // line, turn hover off.  Practically, this shouldn't happen, but it
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // doesn't hurt to be defensive.
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if ((hovered_line_ != kNoMatch) && (result->size() <= hovered_line_))
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetHoveredLine(kNoMatch);
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->UpdatePopupAppearance();
305513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  edit_model_->ResultsUpdated();
3063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  edit_model_->PopupBoundsChangedTo(view_->GetTargetBounds());
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkBitmap* AutocompletePopupModel::GetSpecialIconForMatch(
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const AutocompleteMatch& match) const {
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!match.template_url || !match.template_url->IsExtensionKeyword())
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return &profile_->GetExtensionsService()->GetOmniboxPopupIcon(
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      match.template_url->GetExtensionId());
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
317