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