searchbox.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Copyright 2012 The Chromium Authors. All rights reserved.
2e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Use of this source code is governed by a BSD-style license that can be
3e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// found in the LICENSE file.
4e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
5e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/renderer/searchbox/searchbox.h"
6e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
7e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include <string>
8e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
9e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/strings/string_number_conversions.h"
10e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/strings/string_util.h"
11e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/strings/utf_string_conversions.h"
12e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/common/chrome_switches.h"
13e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/common/omnibox_focus_state.h"
14e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/common/render_messages.h"
15e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/common/url_constants.h"
16e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/renderer/searchbox/searchbox_extension.h"
17e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "content/public/renderer/render_view.h"
18e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "googleurl/src/gurl.h"
19e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "grit/renderer_resources.h"
20e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "net/base/escape.h"
21e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "third_party/WebKit/public/web/WebDocument.h"
22e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "third_party/WebKit/public/web/WebFrame.h"
23e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "third_party/WebKit/public/web/WebView.h"
24e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "ui/base/resource/resource_bundle.h"
25e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
26e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengnamespace {
27e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
28e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Size of the results cache.
29e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengconst size_t kMaxInstantAutocompleteResultItemCacheSize = 100;
30e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
31e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Returns true if items stored in |old_item_id_pairs| and |new_items| are
32e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// equal.
33e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengbool AreMostVisitedItemsEqual(
34e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const std::vector<InstantMostVisitedItemIDPair>& old_item_id_pairs,
35e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const std::vector<InstantMostVisitedItem>& new_items) {
36e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (old_item_id_pairs.size() != new_items.size())
37e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
38e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
39e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  for (size_t i = 0; i < new_items.size(); ++i) {
40e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    if (new_items[i].url != old_item_id_pairs[i].second.url ||
41e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        new_items[i].title != old_item_id_pairs[i].second.title) {
42e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      return false;
43e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    }
44e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
45e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  return true;
46e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
47e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
48e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}  // namespace
49e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
50e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengnamespace internal {  // for testing
51e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
52e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Parses |url| and fills in |id| with the InstantRestrictedID obtained from the
53e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// |url|. |render_view_id| is the ID of the associated RenderView.
54e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng//
55e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Valid |url| forms:
56e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// chrome-search://favicon/<view_id>/<restricted_id>
57e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// chrome-search://thumb/<view_id>/<restricted_id>
58e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng//
59e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// If the |url| is valid, returns true and fills in |id| with restricted_id
60e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// value. If the |url| is invalid, returns false and |id| is not set.
61e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengbool GetInstantRestrictedIDFromURL(int render_view_id,
62e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                   const GURL& url,
63e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                   InstantRestrictedID* id) {
64e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Strip leading path.
65e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  std::string path = url.path().substr(1);
66e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
67e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Check that the path is of Most visited item ID form.
68e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  std::vector<std::string> tokens;
69e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (Tokenize(path, "/", &tokens) != 2)
70e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
71e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
72e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  int view_id = 0;
73e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (!base::StringToInt(tokens[0], &view_id) || view_id != render_view_id)
74e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
75e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  return base::StringToInt(tokens[1], id);
76e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
77e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
78e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}  // namespace internal
79e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
80e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengSearchBox::SearchBox(content::RenderView* render_view)
81e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    : content::RenderViewObserver(render_view),
82e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      content::RenderViewObserverTracker<SearchBox>(render_view),
83e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      verbatim_(false),
84e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      query_is_restricted_(false),
85e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      selection_start_(0),
86e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      selection_end_(0),
87e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      start_margin_(0),
88e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      is_focused_(false),
89e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      is_key_capture_enabled_(false),
90e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      is_input_in_progress_(false),
91e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      display_instant_results_(false),
92e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      omnibox_font_size_(0),
93e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      app_launcher_enabled_(false),
94e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      autocomplete_results_cache_(kMaxInstantAutocompleteResultItemCacheSize),
95e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize) {
96e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
97e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
98e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengSearchBox::~SearchBox() {
99e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
100e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
101e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::SetSuggestions(
102e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const std::vector<InstantSuggestion>& suggestions) {
103e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (!suggestions.empty() &&
104e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      suggestions[0].behavior == INSTANT_COMPLETE_REPLACE) {
105e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    SetQuery(suggestions[0].text, true);
106e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    selection_start_ = selection_end_ = query_.size();
107e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
108e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Explicitly allow empty vector to be sent to the browser.
109e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_SetSuggestions(
110e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(), suggestions));
111e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
112e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
113e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::SetVoiceSearchSupported(bool supported) {
114e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_SetVoiceSearchSupported(
115e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(), supported));
116e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
117e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
118e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::MarkQueryAsRestricted() {
119e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  query_is_restricted_ = true;
120e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  query_.clear();
121e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
122e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
123e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::ShowInstantOverlay(int height, InstantSizeUnits units) {
124e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_ShowInstantOverlay(
125e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(), height,
126e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      units));
127e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
128e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
129e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::FocusOmnibox() {
130e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
131e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
132e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      OMNIBOX_FOCUS_VISIBLE));
133e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
134e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
135e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::StartCapturingKeyStrokes() {
136e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
137e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
138e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      OMNIBOX_FOCUS_INVISIBLE));
139e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
140e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
141e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::StopCapturingKeyStrokes() {
142e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
143e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
144e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      OMNIBOX_FOCUS_NONE));
145e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
146e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
147e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::NavigateToURL(const GURL& url,
148e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              content::PageTransition transition,
149e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              WindowOpenDisposition disposition,
150e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              bool is_search_type) {
151e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_SearchBoxNavigate(
152e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
153e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      url, transition, disposition, is_search_type));
154e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
155e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
156e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::DeleteMostVisitedItem(
157e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    InstantRestrictedID most_visited_item_id) {
158e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(
159e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
160e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      GetURLForMostVisitedItem(most_visited_item_id)));
161e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
162e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
163e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::UndoMostVisitedDeletion(
164e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    InstantRestrictedID most_visited_item_id) {
165e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(new ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(
166e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId(),
167e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      GetURLForMostVisitedItem(most_visited_item_id)));
168e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
169e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
170e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid SearchBox::UndoAllMostVisitedDeletions() {
171e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  render_view()->Send(
172e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      new ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions(
173e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      render_view()->GetRoutingID(), render_view()->GetPageId()));
174}
175
176void SearchBox::ShowBars() {
177  DVLOG(1) << render_view() << " ShowBars";
178  render_view()->Send(new ChromeViewHostMsg_SearchBoxShowBars(
179      render_view()->GetRoutingID(), render_view()->GetPageId()));
180}
181
182void SearchBox::HideBars() {
183  DVLOG(1) << render_view() << " HideBars";
184  render_view()->Send(new ChromeViewHostMsg_SearchBoxHideBars(
185      render_view()->GetRoutingID(), render_view()->GetPageId()));
186}
187
188int SearchBox::GetStartMargin() const {
189  return static_cast<int>(start_margin_ / GetZoom());
190}
191
192gfx::Rect SearchBox::GetPopupBounds() const {
193  double zoom = GetZoom();
194  return gfx::Rect(static_cast<int>(popup_bounds_.x() / zoom),
195                   static_cast<int>(popup_bounds_.y() / zoom),
196                   static_cast<int>(popup_bounds_.width() / zoom),
197                   static_cast<int>(popup_bounds_.height() / zoom));
198}
199
200void SearchBox::GetAutocompleteResults(
201    std::vector<InstantAutocompleteResultIDPair>* results) const {
202  autocomplete_results_cache_.GetCurrentItems(results);
203}
204
205bool SearchBox::GetAutocompleteResultWithID(
206    InstantRestrictedID autocomplete_result_id,
207    InstantAutocompleteResult* result) const {
208  return autocomplete_results_cache_.GetItemWithRestrictedID(
209      autocomplete_result_id, result);
210}
211
212const ThemeBackgroundInfo& SearchBox::GetThemeBackgroundInfo() {
213  return theme_info_;
214}
215
216bool SearchBox::GenerateThumbnailURLFromTransientURL(const GURL& transient_url,
217                                                     GURL* url) const {
218  InstantRestrictedID rid = 0;
219  if (!internal::GetInstantRestrictedIDFromURL(render_view()->GetRoutingID(),
220                                               transient_url, &rid)) {
221    return false;
222  }
223
224  GURL most_visited_item_url(GetURLForMostVisitedItem(rid));
225  if (most_visited_item_url.is_empty())
226    return false;
227  *url = GURL(base::StringPrintf("chrome-search://thumb/%s",
228                                 most_visited_item_url.spec().c_str()));
229  return true;
230}
231
232bool SearchBox::GenerateFaviconURLFromTransientURL(const GURL& transient_url,
233                                                   GURL* url) const {
234  InstantRestrictedID rid = 0;
235  if (!internal::GetInstantRestrictedIDFromURL(render_view()->GetRoutingID(),
236                                               transient_url, &rid)) {
237    return false;
238  }
239
240  GURL most_visited_item_url(GetURLForMostVisitedItem(rid));
241  if (most_visited_item_url.is_empty())
242    return false;
243  *url = GURL(base::StringPrintf("chrome-search://favicon/%s",
244                                 most_visited_item_url.spec().c_str()));
245  return true;
246}
247
248bool SearchBox::OnMessageReceived(const IPC::Message& message) {
249  bool handled = true;
250  IPC_BEGIN_MESSAGE_MAP(SearchBox, message)
251    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxChange, OnChange)
252    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit)
253    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxCancel, OnCancel)
254    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPopupResize, OnPopupResize)
255    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMarginChange, OnMarginChange)
256    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxBarsHidden, OnBarsHidden)
257    IPC_MESSAGE_HANDLER(ChromeViewMsg_DetermineIfPageSupportsInstant,
258                        OnDetermineIfPageSupportsInstant)
259    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxAutocompleteResults,
260                        OnAutocompleteResults)
261    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxUpOrDownKeyPressed,
262                        OnUpOrDownKeyPressed)
263    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxEscKeyPressed, OnEscKeyPressed)
264    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxCancelSelection,
265                        OnCancelSelection)
266    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
267                        OnSetDisplayInstantResults)
268    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged, OnFocusChanged)
269    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress,
270                        OnSetInputInProgress)
271    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
272                        OnThemeChanged)
273    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFontInformation,
274                        OnFontInformationReceived)
275    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation,
276                        OnPromoInformationReceived)
277    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
278                        OnMostVisitedChanged)
279    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxToggleVoiceSearch,
280                        OnToggleVoiceSearch)
281    IPC_MESSAGE_UNHANDLED(handled = false)
282  IPC_END_MESSAGE_MAP()
283  return handled;
284}
285
286void SearchBox::OnChange(const string16& query,
287                         bool verbatim,
288                         size_t selection_start,
289                         size_t selection_end) {
290  SetQuery(query, verbatim);
291  selection_start_ = selection_start;
292  selection_end_ = selection_end;
293
294  // If |query| is empty, this is due to the user backspacing away all the text
295  // in the omnibox, or hitting Escape to restore the "permanent URL", or
296  // switching tabs, etc. In all these cases, there will be no corresponding
297  // OnAutocompleteResults(), so clear the autocomplete results ourselves, by
298  // adding an empty set. Don't notify the page using an "onnativesuggestions"
299  // event, though.
300  if (query.empty()) {
301    autocomplete_results_cache_.AddItems(
302        std::vector<InstantAutocompleteResult>());
303  }
304
305  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
306    DVLOG(1) << render_view() << " OnChange";
307    extensions_v8::SearchBoxExtension::DispatchChange(
308        render_view()->GetWebView()->mainFrame());
309  }
310}
311
312void SearchBox::OnSubmit(const string16& query) {
313  // Submit() is called when the user hits Enter to commit the omnibox text.
314  // If |query| is non-blank, the user committed a search. If it's blank, the
315  // omnibox text was a URL, and the user is navigating to it, in which case
316  // we shouldn't update the |query_| or associated state.
317  if (!query.empty()) {
318    SetQuery(query, true);
319    selection_start_ = selection_end_ = query_.size();
320  }
321
322  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
323    DVLOG(1) << render_view() << " OnSubmit";
324    extensions_v8::SearchBoxExtension::DispatchSubmit(
325        render_view()->GetWebView()->mainFrame());
326  }
327
328  if (!query.empty())
329    Reset();
330}
331
332void SearchBox::OnCancel(const string16& query) {
333  SetQuery(query, true);
334  selection_start_ = selection_end_ = query_.size();
335  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
336    DVLOG(1) << render_view() << " OnCancel";
337    extensions_v8::SearchBoxExtension::DispatchCancel(
338        render_view()->GetWebView()->mainFrame());
339  }
340  Reset();
341}
342
343void SearchBox::OnPopupResize(const gfx::Rect& bounds) {
344  popup_bounds_ = bounds;
345  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
346    DVLOG(1) << render_view() << " OnPopupResize";
347    extensions_v8::SearchBoxExtension::DispatchResize(
348        render_view()->GetWebView()->mainFrame());
349  }
350}
351
352void SearchBox::OnMarginChange(int margin, int width) {
353  start_margin_ = margin;
354
355  // Override only the width parameter of the popup bounds.
356  popup_bounds_.set_width(width);
357
358  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
359    extensions_v8::SearchBoxExtension::DispatchMarginChange(
360        render_view()->GetWebView()->mainFrame());
361  }
362}
363
364void SearchBox::OnBarsHidden() {
365  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
366    extensions_v8::SearchBoxExtension::DispatchBarsHidden(
367        render_view()->GetWebView()->mainFrame());
368  }
369}
370
371void SearchBox::OnDetermineIfPageSupportsInstant() {
372  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
373    bool result = extensions_v8::SearchBoxExtension::PageSupportsInstant(
374        render_view()->GetWebView()->mainFrame());
375    DVLOG(1) << render_view() << " PageSupportsInstant: " << result;
376    render_view()->Send(new ChromeViewHostMsg_InstantSupportDetermined(
377        render_view()->GetRoutingID(), render_view()->GetPageId(), result));
378  }
379}
380
381void SearchBox::OnAutocompleteResults(
382    const std::vector<InstantAutocompleteResult>& results) {
383  autocomplete_results_cache_.AddItems(results);
384  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
385    DVLOG(1) << render_view() << " OnAutocompleteResults";
386    extensions_v8::SearchBoxExtension::DispatchAutocompleteResults(
387        render_view()->GetWebView()->mainFrame());
388  }
389}
390
391void SearchBox::OnUpOrDownKeyPressed(int count) {
392  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
393    DVLOG(1) << render_view() << " OnKeyPress: " << count;
394    extensions_v8::SearchBoxExtension::DispatchUpOrDownKeyPress(
395        render_view()->GetWebView()->mainFrame(), count);
396  }
397}
398
399void SearchBox::OnEscKeyPressed() {
400  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
401    DVLOG(1) << render_view() << " OnEscKeyPressed ";
402    extensions_v8::SearchBoxExtension::DispatchEscKeyPress(
403        render_view()->GetWebView()->mainFrame());
404  }
405}
406
407void SearchBox::OnCancelSelection(const string16& query,
408                                  bool verbatim,
409                                  size_t selection_start,
410                                  size_t selection_end) {
411  SetQuery(query, verbatim);
412  selection_start_ = selection_start;
413  selection_end_ = selection_end;
414  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
415    DVLOG(1) << render_view() << " OnKeyPress ESC";
416    extensions_v8::SearchBoxExtension::DispatchEscKeyPress(
417        render_view()->GetWebView()->mainFrame());
418  }
419}
420
421void SearchBox::OnFocusChanged(OmniboxFocusState new_focus_state,
422                               OmniboxFocusChangeReason reason) {
423  bool key_capture_enabled = new_focus_state == OMNIBOX_FOCUS_INVISIBLE;
424  if (key_capture_enabled != is_key_capture_enabled_) {
425    // Tell the page if the key capture mode changed unless the focus state
426    // changed because of TYPING. This is because in that case, the browser
427    // hasn't really stopped capturing key strokes.
428    //
429    // (More practically, if we don't do this check, the page would receive
430    // onkeycapturechange before the corresponding onchange, and the page would
431    // have no way of telling whether the keycapturechange happened because of
432    // some actual user action or just because they started typing.)
433    if (reason != OMNIBOX_FOCUS_CHANGE_TYPING &&
434        render_view()->GetWebView() &&
435        render_view()->GetWebView()->mainFrame()) {
436      is_key_capture_enabled_ = key_capture_enabled;
437      DVLOG(1) << render_view() << " OnKeyCaptureChange";
438      extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
439          render_view()->GetWebView()->mainFrame());
440    }
441  }
442  bool is_focused = new_focus_state == OMNIBOX_FOCUS_VISIBLE;
443  if (is_focused != is_focused_) {
444    is_focused_ = is_focused;
445    DVLOG(1) << render_view() << " OnFocusChange";
446    if (render_view()->GetWebView() &&
447        render_view()->GetWebView()->mainFrame()) {
448      extensions_v8::SearchBoxExtension::DispatchFocusChange(
449          render_view()->GetWebView()->mainFrame());
450    }
451  }
452}
453
454void SearchBox::OnSetInputInProgress(bool is_input_in_progress) {
455  if (is_input_in_progress_ != is_input_in_progress) {
456    is_input_in_progress_ = is_input_in_progress;
457    DVLOG(1) << render_view() << " OnSetInputInProgress";
458    if (render_view()->GetWebView() &&
459        render_view()->GetWebView()->mainFrame()) {
460      if (is_input_in_progress_) {
461        extensions_v8::SearchBoxExtension::DispatchInputStart(
462            render_view()->GetWebView()->mainFrame());
463      } else {
464        extensions_v8::SearchBoxExtension::DispatchInputCancel(
465            render_view()->GetWebView()->mainFrame());
466      }
467    }
468  }
469}
470
471void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) {
472  display_instant_results_ = display_instant_results;
473}
474
475void SearchBox::OnThemeChanged(const ThemeBackgroundInfo& theme_info) {
476  // Do not send duplicate notifications.
477  if (theme_info_ == theme_info)
478    return;
479
480  theme_info_ = theme_info;
481  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
482    extensions_v8::SearchBoxExtension::DispatchThemeChange(
483        render_view()->GetWebView()->mainFrame());
484  }
485}
486
487void SearchBox::OnFontInformationReceived(const string16& omnibox_font,
488                                          size_t omnibox_font_size) {
489  omnibox_font_ = omnibox_font;
490  omnibox_font_size_ = omnibox_font_size;
491}
492
493void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled) {
494  app_launcher_enabled_ = is_app_launcher_enabled;
495}
496
497double SearchBox::GetZoom() const {
498  WebKit::WebView* web_view = render_view()->GetWebView();
499  if (web_view) {
500    double zoom = WebKit::WebView::zoomLevelToZoomFactor(web_view->zoomLevel());
501    if (zoom != 0)
502      return zoom;
503  }
504  return 1.0;
505}
506
507void SearchBox::Reset() {
508  query_.clear();
509  verbatim_ = false;
510  query_is_restricted_ = false;
511  selection_start_ = 0;
512  selection_end_ = 0;
513  popup_bounds_ = gfx::Rect();
514  start_margin_ = 0;
515  is_focused_ = false;
516  is_key_capture_enabled_ = false;
517  theme_info_ = ThemeBackgroundInfo();
518  // Don't reset display_instant_results_ to prevent clearing it on committed
519  // results pages in extended mode. Otherwise resetting it is a no-op because
520  // a new loader is created when it changes; see crbug.com/164662.
521  // Also don't reset omnibox_font_ or omnibox_font_size_ since it never
522  // changes.
523}
524
525void SearchBox::SetQuery(const string16& query, bool verbatim) {
526  query_ = query;
527  verbatim_ = verbatim;
528  query_is_restricted_ = false;
529}
530
531void SearchBox::OnMostVisitedChanged(
532    const std::vector<InstantMostVisitedItem>& items) {
533  std::vector<InstantMostVisitedItemIDPair> last_known_items;
534  GetMostVisitedItems(&last_known_items);
535
536  if (AreMostVisitedItemsEqual(last_known_items, items))
537    return;  // Do not send duplicate onmostvisitedchange events.
538
539  most_visited_items_cache_.AddItems(items);
540  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
541    extensions_v8::SearchBoxExtension::DispatchMostVisitedChanged(
542        render_view()->GetWebView()->mainFrame());
543  }
544}
545
546void SearchBox::GetMostVisitedItems(
547    std::vector<InstantMostVisitedItemIDPair>* items) const {
548  return most_visited_items_cache_.GetCurrentItems(items);
549}
550
551bool SearchBox::GetMostVisitedItemWithID(
552    InstantRestrictedID most_visited_item_id,
553    InstantMostVisitedItem* item) const {
554  return most_visited_items_cache_.GetItemWithRestrictedID(most_visited_item_id,
555                                                           item);
556}
557
558GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const {
559  InstantMostVisitedItem item;
560  return GetMostVisitedItemWithID(item_id, &item) ? item.url : GURL();
561}
562
563void SearchBox::OnToggleVoiceSearch() {
564  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
565    extensions_v8::SearchBoxExtension::DispatchToggleVoiceSearch(
566        render_view()->GetWebView()->mainFrame());
567  }
568}
569