15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_match.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/search_engines/template_url.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/search_engines/template_url_service.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/search_engines/template_url_service_factory.h"
177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/ui/omnibox/omnibox_popup_model_observer.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/ubidi.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/image/image.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// OmniboxPopupModel
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t OmniboxPopupModel::kNoMatch = -1;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OmniboxPopupModel::OmniboxPopupModel(
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OmniboxPopupView* popup_view,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OmniboxEditModel* edit_model)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : view_(popup_view),
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      edit_model_(edit_model),
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hovered_line_(kNoMatch),
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      selected_line_(kNoMatch),
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      selected_line_state_(NORMAL) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  edit_model->set_popup_model(this);
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OmniboxPopupModel::~OmniboxPopupModel() {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// static
4323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void OmniboxPopupModel::ComputeMatchMaxWidths(int contents_width,
4423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              int separator_width,
4523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              int description_width,
4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              int available_width,
4723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              bool allow_shrinking_contents,
4823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              int* contents_max_width,
4923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                                              int* description_max_width) {
5023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (available_width <= 0) {
5123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    *contents_max_width = 0;
5223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    *description_max_width = 0;
5323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    return;
5423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
5523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
5623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  *contents_max_width = contents_width;
5723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  *description_max_width = description_width;
5823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
5923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // If the description is empty, the contents can get the full width.
6023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (!description_width)
6123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    return;
6223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
6323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  available_width -= separator_width;
6423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
6523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (contents_width + description_width > available_width) {
6623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    if (allow_shrinking_contents) {
6723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      // Try to split the available space fairly between contents and
6823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      // description (if one wants less than half, give it all it wants and
6923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      // give the other the remaining space; otherwise, give each half).
7023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      // However, if this makes the contents too narrow to show a significant
7123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      // amount of information, give the contents more space.
7223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      *contents_max_width = std::max(
7323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          (available_width + 1) / 2, available_width - description_width);
7423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
7523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      const int kMinimumContentsWidth = 300;
7623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      *contents_max_width = std::min(
7723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          std::max(*contents_max_width, kMinimumContentsWidth), contents_width);
7823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
7923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
8023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    // Give the description the remaining space, unless this makes it too small
8123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    // to display anything meaningful, in which case just hide the description
8223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    // and let the contents take up the whole width.
8323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    *description_max_width = available_width - *contents_max_width;
8423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    const int kMinimumDescriptionWidth = 75;
8523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    if (*description_max_width <
8623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        std::min(description_width, kMinimumDescriptionWidth)) {
8723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      *description_max_width = 0;
8823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      *contents_max_width = contents_width;
8923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    }
9023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
9123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
9223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool OmniboxPopupModel::IsOpen() const {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return view_->IsOpen();
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::SetHoveredLine(size_t line) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool is_disabling = (line == kNoMatch);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(is_disabling || (line < result().size()));
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line == hovered_line_)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Nothing to do
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure the old hovered line is redrawn.  No need to redraw the selected
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // line since selection overrides hover so the appearance won't change.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((hovered_line_ != kNoMatch) && (hovered_line_ != selected_line_))
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->InvalidateLine(hovered_line_);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Change the hover to the new line.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hovered_line_ = line;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_disabling && (hovered_line_ != selected_line_))
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->InvalidateLine(hovered_line_);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::SetSelectedLine(size_t line,
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        bool reset_to_default,
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        bool force) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteResult& result = this->result();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result.empty())
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Cancel the query so the matches don't change on the user.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  autocomplete_controller()->Stop(false);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  line = std::min(line, result.size() - 1);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteMatch& match = result.match_at(line);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reset_to_default) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manually_selected_match_.Clear();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Track the user's selection until they cancel it.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manually_selected_match_.destination_url = match.destination_url;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manually_selected_match_.provider_affinity = match.provider;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manually_selected_match_.is_history_what_you_typed_match =
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        match.is_history_what_you_typed_match;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line == selected_line_ && !force)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Nothing else to do.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We need to update |selected_line_state_| and |selected_line_| before
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // calling InvalidateLine(), since it will check them to determine how to
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // draw.  We also need to update |selected_line_| before calling
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // OnPopupDataChanged(), so that when the edit notifies its controller that
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // something has changed, the controller can get the correct updated data.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: We should never reach here with no selected line; the same code that
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opened the popup and made it possible to get here should have also set a
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // selected line.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(selected_line_ != kNoMatch);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL current_destination(result.match_at(selected_line_).destination_url);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t prev_selected_line = selected_line_;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  selected_line_state_ = NORMAL;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  selected_line_ = line;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->InvalidateLine(prev_selected_line);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->InvalidateLine(selected_line_);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the edit with the new data for this match.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(pkasting): If |selected_line_| moves to the controller, this can be
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // eliminated and just become a call to the observer on the edit.
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 keyword;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_keyword_hint;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  match.GetKeywordUIState(edit_model_->profile(), &keyword, &is_keyword_hint);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reset_to_default) {
165ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    edit_model_->OnPopupDataChanged(match.inline_autocompletion, NULL,
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    keyword, is_keyword_hint);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    edit_model_->OnPopupDataChanged(match.fill_into_edit, &current_destination,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    keyword, is_keyword_hint);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Repaint old and new selected lines immediately, so that the edit doesn't
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // appear to update [much] faster than the popup.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->PaintUpdatesNow();
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::ResetToDefaultMatch() {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteResult& result = this->result();
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(!result.empty());
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetSelectedLine(result.default_match() - result.begin(), true, false);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->OnDragCanceled();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::Move(int count) {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteResult& result = this->result();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result.empty())
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The user is using the keyboard to change the selection, so stop tracking
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // hover.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetHoveredLine(kNoMatch);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clamp the new line to [0, result_.count() - 1].
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t new_line = selected_line_ + count;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line,
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  false, false);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::SetSelectedLineState(LineState state) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!result().empty());
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(kNoMatch, selected_line_);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteMatch& match = result().match_at(selected_line_);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(match.associated_keyword.get());
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  selected_line_state_ = state;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->InvalidateLine(selected_line_);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::TryDeletingCurrentItem() {
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // We could use GetInfoForCurrentText() here, but it seems better to try
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and shift-delete the actual selection, rather than any "in progress, not
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // yet visible" one.
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (selected_line_ == kNoMatch)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Cancel the query so the matches don't change on the user.
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  autocomplete_controller()->Stop(false);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteMatch& match = result().match_at(selected_line_);
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (match.SupportsDeletion()) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t selected_line = selected_line_;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const bool was_temporary_text = !manually_selected_match_.empty();
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This will synchronously notify both the edit and us that the results
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // have changed, causing both to revert to the default match.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    autocomplete_controller()->DeleteMatch(match);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AutocompleteResult& result = this->result();
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!result.empty() &&
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (was_temporary_text || selected_line != selected_line_)) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Move the selection to the next choice after the deleted one.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // SetSelectedLine() will clamp to take care of the case where we deleted
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the last item.
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(pkasting): Eventually the controller should take care of this
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // before notifying us, reducing flicker.  At that point the check for
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // deletability can move there too.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetSelectedLine(selected_line, false, true);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Image OmniboxPopupModel::GetIconIfExtensionMatch(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AutocompleteMatch& match) const {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = edit_model_->profile();
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const TemplateURL* template_url = match.GetTemplateURL(profile, false);
2461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (template_url &&
2471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      (template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION)) {
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return extensions::OmniboxAPI::Get(profile)->GetOmniboxPopupIcon(
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        template_url->GetExtensionId());
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Image();
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OmniboxPopupModel::OnResultChanged() {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AutocompleteResult& result = this->result();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  selected_line_ = result.default_match() == result.end() ?
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kNoMatch : static_cast<size_t>(result.default_match() - result.begin());
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There had better not be a nonempty result set with no default match.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK((selected_line_ != kNoMatch) || result.empty());
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  manually_selected_match_.Clear();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  selected_line_state_ = NORMAL;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're going to trim the window size to no longer include the hovered
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // line, turn hover off.  Practically, this shouldn't happen, but it
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // doesn't hurt to be defensive.
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_))
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetHoveredLine(kNoMatch);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  bool popup_was_open = view_->IsOpen();
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_->UpdatePopupAppearance();
2707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // If popup has just been shown or hidden, notify observers.
2717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (view_->IsOpen() != popup_was_open) {
2727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    FOR_EACH_OBSERVER(OmniboxPopupModelObserver, observers_,
2737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      OnOmniboxPopupShownOrHidden());
2747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
2757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
2767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
2777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid OmniboxPopupModel::AddObserver(OmniboxPopupModelObserver* observer) {
2787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  observers_.AddObserver(observer);
2797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
2807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
2817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid OmniboxPopupModel::RemoveObserver(OmniboxPopupModelObserver* observer) {
2827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  observers_.RemoveObserver(observer);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
284