Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / renderer / searchbox / searchbox.cc
blob8d84d8ccfa723934f1a33ef1244c0796cf933644
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.
5 #include "chrome/renderer/searchbox/searchbox.h"
7 #include <string>
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/render_messages.h"
17 #include "chrome/common/url_constants.h"
18 #include "chrome/renderer/searchbox/searchbox_extension.h"
19 #include "components/favicon_base/fallback_icon_url_parser.h"
20 #include "components/favicon_base/favicon_types.h"
21 #include "components/favicon_base/favicon_url_parser.h"
22 #include "components/favicon_base/large_icon_url_parser.h"
23 #include "components/omnibox/common/omnibox_focus_state.h"
24 #include "content/public/renderer/render_frame.h"
25 #include "content/public/renderer/render_view.h"
26 #include "net/base/escape.h"
27 #include "third_party/WebKit/public/web/WebDocument.h"
28 #include "third_party/WebKit/public/web/WebFrame.h"
29 #include "third_party/WebKit/public/web/WebLocalFrame.h"
30 #include "third_party/WebKit/public/web/WebPerformance.h"
31 #include "third_party/WebKit/public/web/WebView.h"
32 #include "url/gurl.h"
34 namespace {
36 // The size of the InstantMostVisitedItem cache.
37 const size_t kMaxInstantMostVisitedItemCacheSize = 100;
39 // Returns true if items stored in |old_item_id_pairs| and |new_items| are
40 // equal.
41 bool AreMostVisitedItemsEqual(
42 const std::vector<InstantMostVisitedItemIDPair>& old_item_id_pairs,
43 const std::vector<InstantMostVisitedItem>& new_items) {
44 if (old_item_id_pairs.size() != new_items.size())
45 return false;
47 for (size_t i = 0; i < new_items.size(); ++i) {
48 if (new_items[i].url != old_item_id_pairs[i].second.url ||
49 new_items[i].title != old_item_id_pairs[i].second.title) {
50 return false;
53 return true;
56 const char* GetIconTypeUrlHost(SearchBox::ImageSourceType type) {
57 switch (type) {
58 case SearchBox::FAVICON:
59 return "favicon";
60 case SearchBox::LARGE_ICON:
61 return "large-icon";
62 case SearchBox::FALLBACK_ICON:
63 return "fallback-icon";
64 case SearchBox::THUMB:
65 return "thumb";
66 default:
67 NOTREACHED();
69 return nullptr;
72 // Given |path| from an image URL, returns starting index of the page URL,
73 // depending on |type| of image URL. Returns -1 if parse fails.
74 int GetImagePathStartOfPageURL(SearchBox::ImageSourceType type,
75 const std::string& path) {
76 // TODO(huangs): Refactor this: http://crbug.com/468320.
77 switch (type) {
78 case SearchBox::FAVICON: {
79 chrome::ParsedFaviconPath parsed;
80 return chrome::ParseFaviconPath(
81 path, favicon_base::FAVICON, &parsed) ? parsed.path_index : -1;
83 case SearchBox::LARGE_ICON: {
84 LargeIconUrlParser parser;
85 return parser.Parse(path) ? parser.path_index() : -1;
87 case SearchBox::FALLBACK_ICON: {
88 chrome::ParsedFallbackIconPath parser;
89 return parser.Parse(path) ? parser.path_index() : -1;
91 case SearchBox::THUMB: {
92 return 0;
94 default: {
95 NOTREACHED();
96 break;
99 return -1;
102 // Helper for SearchBox::GenerateImageURLFromTransientURL().
103 class SearchBoxIconURLHelper: public SearchBox::IconURLHelper {
104 public:
105 explicit SearchBoxIconURLHelper(const SearchBox* search_box);
106 ~SearchBoxIconURLHelper() override;
107 int GetViewID() const override;
108 std::string GetURLStringFromRestrictedID(InstantRestrictedID rid) const
109 override;
111 private:
112 const SearchBox* search_box_;
115 SearchBoxIconURLHelper::SearchBoxIconURLHelper(const SearchBox* search_box)
116 : search_box_(search_box) {
119 SearchBoxIconURLHelper::~SearchBoxIconURLHelper() {
122 int SearchBoxIconURLHelper::GetViewID() const {
123 return search_box_->render_view()->GetRoutingID();
126 std::string SearchBoxIconURLHelper::GetURLStringFromRestrictedID(
127 InstantRestrictedID rid) const {
128 InstantMostVisitedItem item;
129 if (!search_box_->GetMostVisitedItemWithID(rid, &item))
130 return std::string();
132 return item.url.spec();
135 } // namespace
137 namespace internal { // for testing
139 // Parses "<view_id>/<restricted_id>". If successful, assigns
140 // |*view_id| := "<view_id>", |*rid| := "<restricted_id>", and returns true.
141 bool ParseViewIdAndRestrictedId(const std::string& id_part,
142 int* view_id_out,
143 InstantRestrictedID* rid_out) {
144 DCHECK(view_id_out);
145 DCHECK(rid_out);
146 // Check that the path is of Most visited item ID form.
147 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
148 id_part, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
149 if (tokens.size() != 2)
150 return false;
152 int view_id;
153 InstantRestrictedID rid;
154 if (!base::StringToInt(tokens[0], &view_id) || view_id < 0 ||
155 !base::StringToInt(tokens[1], &rid) || rid < 0)
156 return false;
158 *view_id_out = view_id;
159 *rid_out = rid;
160 return true;
163 // Takes icon |url| of given |type|, e.g., FAVICON looking like
165 // chrome-search://favicon/<view_id>/<restricted_id>
166 // chrome-search://favicon/<parameters>/<view_id>/<restricted_id>
168 // If successful, assigns |*param_part| := "" or "<parameters>/" (note trailing
169 // slash), |*view_id| := "<view_id>", |*rid| := "rid", and returns true.
170 bool ParseIconRestrictedUrl(const GURL& url,
171 SearchBox::ImageSourceType type,
172 std::string* param_part,
173 int* view_id,
174 InstantRestrictedID* rid) {
175 DCHECK(param_part);
176 DCHECK(view_id);
177 DCHECK(rid);
178 // Strip leading slash.
179 std::string raw_path = url.path();
180 DCHECK_GT(raw_path.length(), (size_t) 0);
181 DCHECK_EQ(raw_path[0], '/');
182 raw_path = raw_path.substr(1);
184 int path_index = GetImagePathStartOfPageURL(type, raw_path);
185 if (path_index < 0)
186 return false;
188 std::string id_part = raw_path.substr(path_index);
189 if (!ParseViewIdAndRestrictedId(id_part, view_id, rid))
190 return false;
192 *param_part = raw_path.substr(0, path_index);
193 return true;
196 bool TranslateIconRestrictedUrl(const GURL& transient_url,
197 SearchBox::ImageSourceType type,
198 const SearchBox::IconURLHelper& helper,
199 GURL* url) {
200 std::string params;
201 int view_id = -1;
202 InstantRestrictedID rid = -1;
204 if (!internal::ParseIconRestrictedUrl(
205 transient_url, type, &params, &view_id, &rid) ||
206 view_id != helper.GetViewID()) {
207 if (type == SearchBox::FAVICON) {
208 *url = GURL(base::StringPrintf("chrome-search://%s/",
209 GetIconTypeUrlHost(SearchBox::FAVICON)));
210 return true;
212 return false;
215 std::string item_url = helper.GetURLStringFromRestrictedID(rid);
216 *url = GURL(base::StringPrintf("chrome-search://%s/%s%s",
217 GetIconTypeUrlHost(type),
218 params.c_str(),
219 item_url.c_str()));
220 return true;
223 } // namespace internal
225 SearchBox::IconURLHelper::IconURLHelper() {
228 SearchBox::IconURLHelper::~IconURLHelper() {
231 SearchBox::SearchBox(content::RenderView* render_view)
232 : content::RenderViewObserver(render_view),
233 content::RenderViewObserverTracker<SearchBox>(render_view),
234 page_seq_no_(0),
235 app_launcher_enabled_(false),
236 is_focused_(false),
237 is_input_in_progress_(false),
238 is_key_capture_enabled_(false),
239 display_instant_results_(false),
240 most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize),
241 query_(),
242 start_margin_(0) {
245 SearchBox::~SearchBox() {
248 void SearchBox::LogEvent(NTPLoggingEventType event) {
249 // The main frame for the current RenderView may be out-of-process, in which
250 // case it won't have performance(). Use the default delta of 0 in this
251 // case.
252 base::TimeDelta delta;
253 if (render_view()->GetWebView()->mainFrame()->isWebLocalFrame()) {
254 // navigation_start in ms.
255 uint64 start = 1000 * (render_view()->GetMainRenderFrame()->GetWebFrame()->
256 performance().navigationStart());
257 uint64 now = (base::TimeTicks::Now() - base::TimeTicks::UnixEpoch())
258 .InMilliseconds();
259 DCHECK(now >= start);
260 delta = base::TimeDelta::FromMilliseconds(now - start);
262 render_view()->Send(new ChromeViewHostMsg_LogEvent(
263 render_view()->GetRoutingID(), page_seq_no_, event, delta));
266 void SearchBox::LogMostVisitedImpression(int position,
267 const base::string16& provider) {
268 render_view()->Send(new ChromeViewHostMsg_LogMostVisitedImpression(
269 render_view()->GetRoutingID(), page_seq_no_, position, provider));
272 void SearchBox::LogMostVisitedNavigation(int position,
273 const base::string16& provider) {
274 render_view()->Send(new ChromeViewHostMsg_LogMostVisitedNavigation(
275 render_view()->GetRoutingID(), page_seq_no_, position, provider));
278 void SearchBox::CheckIsUserSignedInToChromeAs(const base::string16& identity) {
279 render_view()->Send(new ChromeViewHostMsg_ChromeIdentityCheck(
280 render_view()->GetRoutingID(), page_seq_no_, identity));
283 void SearchBox::CheckIsUserSyncingHistory() {
284 render_view()->Send(new ChromeViewHostMsg_HistorySyncCheck(
285 render_view()->GetRoutingID(), page_seq_no_));
288 void SearchBox::DeleteMostVisitedItem(
289 InstantRestrictedID most_visited_item_id) {
290 render_view()->Send(new ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(
291 render_view()->GetRoutingID(),
292 page_seq_no_,
293 GetURLForMostVisitedItem(most_visited_item_id)));
296 bool SearchBox::GenerateImageURLFromTransientURL(const GURL& transient_url,
297 ImageSourceType type,
298 GURL* url) const {
299 SearchBoxIconURLHelper helper(this);
300 return internal::TranslateIconRestrictedUrl(transient_url, type, helper, url);
303 void SearchBox::GetMostVisitedItems(
304 std::vector<InstantMostVisitedItemIDPair>* items) const {
305 return most_visited_items_cache_.GetCurrentItems(items);
308 bool SearchBox::GetMostVisitedItemWithID(
309 InstantRestrictedID most_visited_item_id,
310 InstantMostVisitedItem* item) const {
311 return most_visited_items_cache_.GetItemWithRestrictedID(most_visited_item_id,
312 item);
315 const ThemeBackgroundInfo& SearchBox::GetThemeBackgroundInfo() {
316 return theme_info_;
319 const EmbeddedSearchRequestParams& SearchBox::GetEmbeddedSearchRequestParams() {
320 return embedded_search_request_params_;
323 void SearchBox::Focus() {
324 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
325 render_view()->GetRoutingID(), page_seq_no_, OMNIBOX_FOCUS_VISIBLE));
328 void SearchBox::NavigateToURL(const GURL& url,
329 WindowOpenDisposition disposition,
330 bool is_most_visited_item_url) {
331 render_view()->Send(new ChromeViewHostMsg_SearchBoxNavigate(
332 render_view()->GetRoutingID(), page_seq_no_, url,
333 disposition, is_most_visited_item_url));
336 void SearchBox::Paste(const base::string16& text) {
337 render_view()->Send(new ChromeViewHostMsg_PasteAndOpenDropdown(
338 render_view()->GetRoutingID(), page_seq_no_, text));
341 void SearchBox::SetVoiceSearchSupported(bool supported) {
342 render_view()->Send(new ChromeViewHostMsg_SetVoiceSearchSupported(
343 render_view()->GetRoutingID(), page_seq_no_, supported));
346 void SearchBox::StartCapturingKeyStrokes() {
347 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
348 render_view()->GetRoutingID(), page_seq_no_, OMNIBOX_FOCUS_INVISIBLE));
351 void SearchBox::StopCapturingKeyStrokes() {
352 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
353 render_view()->GetRoutingID(), page_seq_no_, OMNIBOX_FOCUS_NONE));
356 void SearchBox::UndoAllMostVisitedDeletions() {
357 render_view()->Send(
358 new ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions(
359 render_view()->GetRoutingID(), page_seq_no_));
362 void SearchBox::UndoMostVisitedDeletion(
363 InstantRestrictedID most_visited_item_id) {
364 render_view()->Send(new ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(
365 render_view()->GetRoutingID(), page_seq_no_,
366 GetURLForMostVisitedItem(most_visited_item_id)));
369 bool SearchBox::OnMessageReceived(const IPC::Message& message) {
370 bool handled = true;
371 IPC_BEGIN_MESSAGE_MAP(SearchBox, message)
372 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetPageSequenceNumber,
373 OnSetPageSequenceNumber)
374 IPC_MESSAGE_HANDLER(ChromeViewMsg_ChromeIdentityCheckResult,
375 OnChromeIdentityCheckResult)
376 IPC_MESSAGE_HANDLER(ChromeViewMsg_DetermineIfPageSupportsInstant,
377 OnDetermineIfPageSupportsInstant)
378 IPC_MESSAGE_HANDLER(ChromeViewMsg_HistorySyncCheckResult,
379 OnHistorySyncCheckResult)
380 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged, OnFocusChanged)
381 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMarginChange, OnMarginChange)
382 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
383 OnMostVisitedChanged)
384 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation,
385 OnPromoInformationReceived)
386 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
387 OnSetDisplayInstantResults)
388 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress,
389 OnSetInputInProgress)
390 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch,
391 OnSetSuggestionToPrefetch)
392 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit)
393 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
394 OnThemeChanged)
395 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxToggleVoiceSearch,
396 OnToggleVoiceSearch)
397 IPC_MESSAGE_UNHANDLED(handled = false)
398 IPC_END_MESSAGE_MAP()
399 return handled;
402 void SearchBox::OnSetPageSequenceNumber(int page_seq_no) {
403 page_seq_no_ = page_seq_no;
406 void SearchBox::OnChromeIdentityCheckResult(const base::string16& identity,
407 bool identity_match) {
408 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
409 extensions_v8::SearchBoxExtension::DispatchChromeIdentityCheckResult(
410 render_view()->GetWebView()->mainFrame(), identity, identity_match);
414 void SearchBox::OnDetermineIfPageSupportsInstant() {
415 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
416 bool result = extensions_v8::SearchBoxExtension::PageSupportsInstant(
417 render_view()->GetWebView()->mainFrame());
418 DVLOG(1) << render_view() << " PageSupportsInstant: " << result;
419 render_view()->Send(new ChromeViewHostMsg_InstantSupportDetermined(
420 render_view()->GetRoutingID(), page_seq_no_, result));
424 void SearchBox::OnFocusChanged(OmniboxFocusState new_focus_state,
425 OmniboxFocusChangeReason reason) {
426 bool key_capture_enabled = new_focus_state == OMNIBOX_FOCUS_INVISIBLE;
427 if (key_capture_enabled != is_key_capture_enabled_) {
428 // Tell the page if the key capture mode changed unless the focus state
429 // changed because of TYPING. This is because in that case, the browser
430 // hasn't really stopped capturing key strokes.
432 // (More practically, if we don't do this check, the page would receive
433 // onkeycapturechange before the corresponding onchange, and the page would
434 // have no way of telling whether the keycapturechange happened because of
435 // some actual user action or just because they started typing.)
436 if (reason != OMNIBOX_FOCUS_CHANGE_TYPING &&
437 render_view()->GetWebView() &&
438 render_view()->GetWebView()->mainFrame()) {
439 is_key_capture_enabled_ = key_capture_enabled;
440 DVLOG(1) << render_view() << " OnKeyCaptureChange";
441 extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
442 render_view()->GetWebView()->mainFrame());
445 bool is_focused = new_focus_state == OMNIBOX_FOCUS_VISIBLE;
446 if (is_focused != is_focused_) {
447 is_focused_ = is_focused;
448 DVLOG(1) << render_view() << " OnFocusChange";
449 if (render_view()->GetWebView() &&
450 render_view()->GetWebView()->mainFrame()) {
451 extensions_v8::SearchBoxExtension::DispatchFocusChange(
452 render_view()->GetWebView()->mainFrame());
457 void SearchBox::OnHistorySyncCheckResult(bool sync_history) {
458 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
459 extensions_v8::SearchBoxExtension::DispatchHistorySyncCheckResult(
460 render_view()->GetWebView()->mainFrame(), sync_history);
464 void SearchBox::OnMarginChange(int margin) {
465 start_margin_ = margin;
466 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
467 extensions_v8::SearchBoxExtension::DispatchMarginChange(
468 render_view()->GetWebView()->mainFrame());
472 void SearchBox::OnMostVisitedChanged(
473 const std::vector<InstantMostVisitedItem>& items) {
474 std::vector<InstantMostVisitedItemIDPair> last_known_items;
475 GetMostVisitedItems(&last_known_items);
477 if (AreMostVisitedItemsEqual(last_known_items, items))
478 return; // Do not send duplicate onmostvisitedchange events.
480 most_visited_items_cache_.AddItems(items);
481 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
482 extensions_v8::SearchBoxExtension::DispatchMostVisitedChanged(
483 render_view()->GetWebView()->mainFrame());
487 void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled) {
488 app_launcher_enabled_ = is_app_launcher_enabled;
491 void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) {
492 display_instant_results_ = display_instant_results;
495 void SearchBox::OnSetInputInProgress(bool is_input_in_progress) {
496 if (is_input_in_progress_ != is_input_in_progress) {
497 is_input_in_progress_ = is_input_in_progress;
498 DVLOG(1) << render_view() << " OnSetInputInProgress";
499 if (render_view()->GetWebView() &&
500 render_view()->GetWebView()->mainFrame()) {
501 if (is_input_in_progress_) {
502 extensions_v8::SearchBoxExtension::DispatchInputStart(
503 render_view()->GetWebView()->mainFrame());
504 } else {
505 extensions_v8::SearchBoxExtension::DispatchInputCancel(
506 render_view()->GetWebView()->mainFrame());
512 void SearchBox::OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion) {
513 suggestion_ = suggestion;
514 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
515 DVLOG(1) << render_view() << " OnSetSuggestionToPrefetch";
516 extensions_v8::SearchBoxExtension::DispatchSuggestionChange(
517 render_view()->GetWebView()->mainFrame());
521 void SearchBox::OnSubmit(const base::string16& query,
522 const EmbeddedSearchRequestParams& params) {
523 query_ = query;
524 embedded_search_request_params_ = params;
525 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
526 DVLOG(1) << render_view() << " OnSubmit";
527 extensions_v8::SearchBoxExtension::DispatchSubmit(
528 render_view()->GetWebView()->mainFrame());
530 if (!query.empty())
531 Reset();
534 void SearchBox::OnThemeChanged(const ThemeBackgroundInfo& theme_info) {
535 // Do not send duplicate notifications.
536 if (theme_info_ == theme_info)
537 return;
539 theme_info_ = theme_info;
540 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
541 extensions_v8::SearchBoxExtension::DispatchThemeChange(
542 render_view()->GetWebView()->mainFrame());
546 void SearchBox::OnToggleVoiceSearch() {
547 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
548 extensions_v8::SearchBoxExtension::DispatchToggleVoiceSearch(
549 render_view()->GetWebView()->mainFrame());
553 GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const {
554 InstantMostVisitedItem item;
555 return GetMostVisitedItemWithID(item_id, &item) ? item.url : GURL();
558 void SearchBox::Reset() {
559 query_.clear();
560 embedded_search_request_params_ = EmbeddedSearchRequestParams();
561 suggestion_ = InstantSuggestion();
562 start_margin_ = 0;
563 is_focused_ = false;
564 is_key_capture_enabled_ = false;
565 theme_info_ = ThemeBackgroundInfo();