searchbox.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/renderer/searchbox/searchbox.h" 6 7#include "base/string_number_conversions.h" 8#include "base/utf_string_conversions.h" 9#include "chrome/common/chrome_switches.h" 10#include "chrome/common/omnibox_focus_state.h" 11#include "chrome/common/render_messages.h" 12#include "chrome/common/url_constants.h" 13#include "chrome/renderer/searchbox/searchbox_extension.h" 14#include "content/public/renderer/render_view.h" 15#include "grit/renderer_resources.h" 16#include "net/base/escape.h" 17#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" 18#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 19#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 20#include "ui/base/resource/resource_bundle.h" 21 22namespace { 23 24// Size of the results cache. 25const size_t kMaxInstantAutocompleteResultItemCacheSize = 100; 26 27bool IsThemeInfoEqual(const ThemeBackgroundInfo& new_theme_info, 28 const ThemeBackgroundInfo& old_theme_info) { 29 return old_theme_info.color_r == new_theme_info.color_r && 30 old_theme_info.color_g == new_theme_info.color_g && 31 old_theme_info.color_b == new_theme_info.color_b && 32 old_theme_info.color_a == new_theme_info.color_a && 33 old_theme_info.theme_id == new_theme_info.theme_id && 34 old_theme_info.image_horizontal_alignment == 35 new_theme_info.image_horizontal_alignment && 36 old_theme_info.image_vertical_alignment == 37 new_theme_info.image_vertical_alignment && 38 old_theme_info.image_tiling == new_theme_info.image_tiling && 39 old_theme_info.image_height == new_theme_info.image_height && 40 old_theme_info.has_attribution == new_theme_info.has_attribution; 41} 42 43bool AreMostVisitedItemsEqual( 44 const std::vector<InstantMostVisitedItemIDPair>& new_items, 45 const std::vector<InstantMostVisitedItemIDPair>& old_items) { 46 if (old_items.size() != new_items.size()) 47 return false; 48 for (size_t i = 0; i < new_items.size(); i++) { 49 const InstantMostVisitedItem& old_item = old_items[i].second; 50 const InstantMostVisitedItem& new_item = new_items[i].second; 51 if (new_item.url != old_item.url || new_item.title != old_item.title) 52 return false; 53 } 54 return true; 55} 56 57} // namespace 58 59SearchBox::SearchBox(content::RenderView* render_view) 60 : content::RenderViewObserver(render_view), 61 content::RenderViewObserverTracker<SearchBox>(render_view), 62 verbatim_(false), 63 query_is_restricted_(false), 64 selection_start_(0), 65 selection_end_(0), 66 start_margin_(0), 67 is_key_capture_enabled_(false), 68 display_instant_results_(false), 69 omnibox_font_size_(0), 70 autocomplete_results_cache_(kMaxInstantAutocompleteResultItemCacheSize), 71 most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize) { 72} 73 74SearchBox::~SearchBox() { 75} 76 77void SearchBox::SetSuggestions( 78 const std::vector<InstantSuggestion>& suggestions) { 79 if (!suggestions.empty() && 80 suggestions[0].behavior == INSTANT_COMPLETE_REPLACE) { 81 SetQuery(suggestions[0].text, true); 82 selection_start_ = selection_end_ = query_.size(); 83 } 84 // Explicitly allow empty vector to be sent to the browser. 85 render_view()->Send(new ChromeViewHostMsg_SetSuggestions( 86 render_view()->GetRoutingID(), render_view()->GetPageId(), suggestions)); 87} 88 89void SearchBox::MarkQueryAsRestricted() { 90 query_is_restricted_ = true; 91 query_.clear(); 92} 93 94void SearchBox::ShowInstantOverlay(int height, InstantSizeUnits units) { 95 render_view()->Send(new ChromeViewHostMsg_ShowInstantOverlay( 96 render_view()->GetRoutingID(), render_view()->GetPageId(), height, 97 units)); 98} 99 100void SearchBox::FocusOmnibox() { 101 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox( 102 render_view()->GetRoutingID(), render_view()->GetPageId(), 103 OMNIBOX_FOCUS_VISIBLE)); 104} 105 106void SearchBox::StartCapturingKeyStrokes() { 107 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox( 108 render_view()->GetRoutingID(), render_view()->GetPageId(), 109 OMNIBOX_FOCUS_INVISIBLE)); 110} 111 112void SearchBox::StopCapturingKeyStrokes() { 113 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox( 114 render_view()->GetRoutingID(), render_view()->GetPageId(), 115 OMNIBOX_FOCUS_NONE)); 116} 117 118void SearchBox::NavigateToURL(const GURL& url, 119 content::PageTransition transition, 120 WindowOpenDisposition disposition, 121 bool is_search_type) { 122 render_view()->Send(new ChromeViewHostMsg_SearchBoxNavigate( 123 render_view()->GetRoutingID(), render_view()->GetPageId(), 124 url, transition, disposition, is_search_type)); 125} 126 127void SearchBox::DeleteMostVisitedItem( 128 InstantRestrictedID most_visited_item_id) { 129 render_view()->Send(new ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem( 130 render_view()->GetRoutingID(), most_visited_item_id)); 131} 132 133void SearchBox::UndoMostVisitedDeletion( 134 InstantRestrictedID most_visited_item_id) { 135 render_view()->Send(new ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion( 136 render_view()->GetRoutingID(), most_visited_item_id)); 137} 138 139void SearchBox::UndoAllMostVisitedDeletions() { 140 render_view()->Send( 141 new ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( 142 render_view()->GetRoutingID())); 143} 144 145void SearchBox::ShowBars() { 146 DVLOG(1) << render_view() << " ShowBars"; 147 render_view()->Send(new ChromeViewHostMsg_SearchBoxShowBars( 148 render_view()->GetRoutingID(), render_view()->GetPageId())); 149} 150 151void SearchBox::HideBars() { 152 DVLOG(1) << render_view() << " HideBars"; 153 render_view()->Send(new ChromeViewHostMsg_SearchBoxHideBars( 154 render_view()->GetRoutingID(), render_view()->GetPageId())); 155} 156 157int SearchBox::GetStartMargin() const { 158 return static_cast<int>(start_margin_ / GetZoom()); 159} 160 161gfx::Rect SearchBox::GetPopupBounds() const { 162 double zoom = GetZoom(); 163 return gfx::Rect(static_cast<int>(popup_bounds_.x() / zoom), 164 static_cast<int>(popup_bounds_.y() / zoom), 165 static_cast<int>(popup_bounds_.width() / zoom), 166 static_cast<int>(popup_bounds_.height() / zoom)); 167} 168 169void SearchBox::GetAutocompleteResults( 170 std::vector<InstantAutocompleteResultIDPair>* results) const { 171 autocomplete_results_cache_.GetCurrentItems(results); 172} 173 174bool SearchBox::GetAutocompleteResultWithID( 175 InstantRestrictedID autocomplete_result_id, 176 InstantAutocompleteResult* result) const { 177 return autocomplete_results_cache_.GetItemWithRestrictedID( 178 autocomplete_result_id, result); 179} 180 181const ThemeBackgroundInfo& SearchBox::GetThemeBackgroundInfo() { 182 return theme_info_; 183} 184 185bool SearchBox::OnMessageReceived(const IPC::Message& message) { 186 bool handled = true; 187 IPC_BEGIN_MESSAGE_MAP(SearchBox, message) 188 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxChange, OnChange) 189 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit) 190 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxCancel, OnCancel) 191 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPopupResize, OnPopupResize) 192 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMarginChange, OnMarginChange) 193 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxBarsHidden, OnBarsHidden) 194 IPC_MESSAGE_HANDLER(ChromeViewMsg_DetermineIfPageSupportsInstant, 195 OnDetermineIfPageSupportsInstant) 196 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxAutocompleteResults, 197 OnAutocompleteResults) 198 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxUpOrDownKeyPressed, 199 OnUpOrDownKeyPressed) 200 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxEscKeyPressed, OnEscKeyPressed) 201 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxCancelSelection, 202 OnCancelSelection) 203 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults, 204 OnSetDisplayInstantResults) 205 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxKeyCaptureChanged, 206 OnKeyCaptureChange) 207 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged, 208 OnThemeChanged) 209 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFontInformation, 210 OnFontInformationReceived) 211 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged, 212 OnMostVisitedChanged) 213 IPC_MESSAGE_UNHANDLED(handled = false) 214 IPC_END_MESSAGE_MAP() 215 return handled; 216} 217 218void SearchBox::DidClearWindowObject(WebKit::WebFrame* frame) { 219 extensions_v8::SearchBoxExtension::DispatchOnWindowReady(frame); 220} 221 222void SearchBox::OnChange(const string16& query, 223 bool verbatim, 224 size_t selection_start, 225 size_t selection_end) { 226 SetQuery(query, verbatim); 227 selection_start_ = selection_start; 228 selection_end_ = selection_end; 229 230 // If |query| is empty, this is due to the user backspacing away all the text 231 // in the omnibox, or hitting Escape to restore the "permanent URL", or 232 // switching tabs, etc. In all these cases, there will be no corresponding 233 // OnAutocompleteResults(), so clear the autocomplete results ourselves, by 234 // adding an empty set. Don't notify the page using an "onnativesuggestions" 235 // event, though. 236 if (query.empty()) { 237 autocomplete_results_cache_.AddItems( 238 std::vector<InstantAutocompleteResult>()); 239 } 240 241 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 242 DVLOG(1) << render_view() << " OnChange"; 243 extensions_v8::SearchBoxExtension::DispatchChange( 244 render_view()->GetWebView()->mainFrame()); 245 } 246} 247 248void SearchBox::OnSubmit(const string16& query) { 249 // Submit() is called when the user hits Enter to commit the omnibox text. 250 // If |query| is non-blank, the user committed a search. If it's blank, the 251 // omnibox text was a URL, and the user is navigating to it, in which case 252 // we shouldn't update the |query_| or associated state. 253 if (!query.empty()) { 254 SetQuery(query, true); 255 selection_start_ = selection_end_ = query_.size(); 256 } 257 258 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 259 DVLOG(1) << render_view() << " OnSubmit"; 260 extensions_v8::SearchBoxExtension::DispatchSubmit( 261 render_view()->GetWebView()->mainFrame()); 262 } 263 264 if (!query.empty()) 265 Reset(); 266} 267 268void SearchBox::OnCancel(const string16& query) { 269 SetQuery(query, true); 270 selection_start_ = selection_end_ = query_.size(); 271 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 272 DVLOG(1) << render_view() << " OnCancel"; 273 extensions_v8::SearchBoxExtension::DispatchCancel( 274 render_view()->GetWebView()->mainFrame()); 275 } 276 Reset(); 277} 278 279void SearchBox::OnPopupResize(const gfx::Rect& bounds) { 280 popup_bounds_ = bounds; 281 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 282 DVLOG(1) << render_view() << " OnPopupResize"; 283 extensions_v8::SearchBoxExtension::DispatchResize( 284 render_view()->GetWebView()->mainFrame()); 285 } 286} 287 288void SearchBox::OnMarginChange(int margin, int width) { 289 start_margin_ = margin; 290 291 // Override only the width parameter of the popup bounds. 292 popup_bounds_.set_width(width); 293 294 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 295 extensions_v8::SearchBoxExtension::DispatchMarginChange( 296 render_view()->GetWebView()->mainFrame()); 297 } 298} 299 300void SearchBox::OnBarsHidden() { 301 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 302 extensions_v8::SearchBoxExtension::DispatchBarsHidden( 303 render_view()->GetWebView()->mainFrame()); 304 } 305} 306 307void SearchBox::OnDetermineIfPageSupportsInstant() { 308 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 309 bool result = extensions_v8::SearchBoxExtension::PageSupportsInstant( 310 render_view()->GetWebView()->mainFrame()); 311 DVLOG(1) << render_view() << " PageSupportsInstant: " << result; 312 render_view()->Send(new ChromeViewHostMsg_InstantSupportDetermined( 313 render_view()->GetRoutingID(), render_view()->GetPageId(), result)); 314 } 315} 316 317void SearchBox::OnAutocompleteResults( 318 const std::vector<InstantAutocompleteResult>& results) { 319 autocomplete_results_cache_.AddItems(results); 320 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 321 DVLOG(1) << render_view() << " OnAutocompleteResults"; 322 extensions_v8::SearchBoxExtension::DispatchAutocompleteResults( 323 render_view()->GetWebView()->mainFrame()); 324 } 325} 326 327void SearchBox::OnUpOrDownKeyPressed(int count) { 328 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 329 DVLOG(1) << render_view() << " OnKeyPress: " << count; 330 extensions_v8::SearchBoxExtension::DispatchUpOrDownKeyPress( 331 render_view()->GetWebView()->mainFrame(), count); 332 } 333} 334 335void SearchBox::OnEscKeyPressed() { 336 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 337 DVLOG(1) << render_view() << " OnEscKeyPressed "; 338 extensions_v8::SearchBoxExtension::DispatchEscKeyPress( 339 render_view()->GetWebView()->mainFrame()); 340 } 341} 342 343void SearchBox::OnCancelSelection(const string16& query, 344 bool verbatim, 345 size_t selection_start, 346 size_t selection_end) { 347 SetQuery(query, verbatim); 348 selection_start_ = selection_start; 349 selection_end_ = selection_end; 350 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 351 DVLOG(1) << render_view() << " OnKeyPress ESC"; 352 extensions_v8::SearchBoxExtension::DispatchEscKeyPress( 353 render_view()->GetWebView()->mainFrame()); 354 } 355} 356 357void SearchBox::OnKeyCaptureChange(bool is_key_capture_enabled) { 358 if (is_key_capture_enabled != is_key_capture_enabled_ && 359 render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 360 is_key_capture_enabled_ = is_key_capture_enabled; 361 DVLOG(1) << render_view() << " OnKeyCaptureChange"; 362 extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange( 363 render_view()->GetWebView()->mainFrame()); 364 } 365} 366 367void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) { 368 display_instant_results_ = display_instant_results; 369} 370 371void SearchBox::OnThemeChanged(const ThemeBackgroundInfo& theme_info) { 372 if (IsThemeInfoEqual(theme_info, theme_info_)) 373 return; 374 theme_info_ = theme_info; 375 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 376 extensions_v8::SearchBoxExtension::DispatchThemeChange( 377 render_view()->GetWebView()->mainFrame()); 378 } 379} 380 381void SearchBox::OnFontInformationReceived(const string16& omnibox_font, 382 size_t omnibox_font_size) { 383 omnibox_font_ = omnibox_font; 384 omnibox_font_size_ = omnibox_font_size; 385} 386 387double SearchBox::GetZoom() const { 388 WebKit::WebView* web_view = render_view()->GetWebView(); 389 if (web_view) { 390 double zoom = WebKit::WebView::zoomLevelToZoomFactor(web_view->zoomLevel()); 391 if (zoom != 0) 392 return zoom; 393 } 394 return 1.0; 395} 396 397void SearchBox::Reset() { 398 query_.clear(); 399 verbatim_ = false; 400 query_is_restricted_ = false; 401 selection_start_ = 0; 402 selection_end_ = 0; 403 popup_bounds_ = gfx::Rect(); 404 start_margin_ = 0; 405 is_key_capture_enabled_ = false; 406 theme_info_ = ThemeBackgroundInfo(); 407 // Don't reset display_instant_results_ to prevent clearing it on committed 408 // results pages in extended mode. Otherwise resetting it is a no-op because 409 // a new loader is created when it changes; see crbug.com/164662. 410 // Also don't reset omnibox_font_ or omnibox_font_size_ since it never 411 // changes. 412} 413 414void SearchBox::SetQuery(const string16& query, bool verbatim) { 415 query_ = query; 416 verbatim_ = verbatim; 417 query_is_restricted_ = false; 418} 419 420void SearchBox::OnMostVisitedChanged( 421 const std::vector<InstantMostVisitedItemIDPair>& items) { 422 std::vector<InstantMostVisitedItemIDPair> old_items; 423 most_visited_items_cache_.GetCurrentItems(&old_items); 424 if (AreMostVisitedItemsEqual(items, old_items)) 425 return; 426 427 most_visited_items_cache_.AddItemsWithRestrictedID(items); 428 429 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { 430 extensions_v8::SearchBoxExtension::DispatchMostVisitedChanged( 431 render_view()->GetWebView()->mainFrame()); 432 } 433} 434 435void SearchBox::GetMostVisitedItems( 436 std::vector<InstantMostVisitedItemIDPair>* items) const { 437 return most_visited_items_cache_.GetCurrentItems(items); 438} 439 440bool SearchBox::GetMostVisitedItemWithID( 441 InstantRestrictedID most_visited_item_id, 442 InstantMostVisitedItem* item) const { 443 return most_visited_items_cache_.GetItemWithRestrictedID(most_visited_item_id, 444 item); 445} 446