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"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/favicon/fallback_icon_url_parser.h"
16 #include "chrome/common/favicon/favicon_url_parser.h"
17 #include "chrome/common/favicon/large_icon_url_parser.h"
18 #include "chrome/common/omnibox_focus_state.h"
19 #include "chrome/common/render_messages.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/renderer/searchbox/searchbox_extension.h"
22 #include "components/favicon_base/favicon_types.h"
23 #include "content/public/renderer/render_frame.h"
24 #include "content/public/renderer/render_view.h"
25 #include "net/base/escape.h"
26 #include "third_party/WebKit/public/web/WebDocument.h"
27 #include "third_party/WebKit/public/web/WebFrame.h"
28 #include "third_party/WebKit/public/web/WebLocalFrame.h"
29 #include "third_party/WebKit/public/web/WebPerformance.h"
30 #include "third_party/WebKit/public/web/WebView.h"
35 // The size of the InstantMostVisitedItem cache.
36 const size_t kMaxInstantMostVisitedItemCacheSize
= 100;
38 // Returns true if items stored in |old_item_id_pairs| and |new_items| are
40 bool AreMostVisitedItemsEqual(
41 const std::vector
<InstantMostVisitedItemIDPair
>& old_item_id_pairs
,
42 const std::vector
<InstantMostVisitedItem
>& new_items
) {
43 if (old_item_id_pairs
.size() != new_items
.size())
46 for (size_t i
= 0; i
< new_items
.size(); ++i
) {
47 if (new_items
[i
].url
!= old_item_id_pairs
[i
].second
.url
||
48 new_items
[i
].title
!= old_item_id_pairs
[i
].second
.title
) {
55 const char* GetIconTypeUrlHost(SearchBox::ImageSourceType type
) {
57 case SearchBox::FAVICON
:
59 case SearchBox::LARGE_ICON
:
61 case SearchBox::FALLBACK_ICON
:
62 return "fallback-icon";
63 case SearchBox::THUMB
:
71 // Given |path| from an image URL, returns starting index of the page URL,
72 // depending on |type| of image URL. Returns -1 if parse fails.
73 int GetImagePathStartOfPageURL(SearchBox::ImageSourceType type
,
74 const std::string
& path
) {
75 // TODO(huangs): Refactor this: http://crbug.com/468320.
77 case SearchBox::FAVICON
: {
78 chrome::ParsedFaviconPath parsed
;
79 return chrome::ParseFaviconPath(
80 path
, favicon_base::FAVICON
, &parsed
) ? parsed
.path_index
: -1;
82 case SearchBox::LARGE_ICON
: {
83 LargeIconUrlParser parser
;
84 return parser
.Parse(path
) ? parser
.path_index() : -1;
86 case SearchBox::FALLBACK_ICON
: {
87 chrome::ParsedFallbackIconPath parser
;
88 return parser
.Parse(path
) ? parser
.path_index() : -1;
90 case SearchBox::THUMB
: {
101 // Helper for SearchBox::GenerateImageURLFromTransientURL().
102 class SearchBoxIconURLHelper
: public SearchBox::IconURLHelper
{
104 explicit SearchBoxIconURLHelper(const SearchBox
* search_box
);
105 ~SearchBoxIconURLHelper() override
;
106 int GetViewID() const override
;
107 std::string
GetURLStringFromRestrictedID(InstantRestrictedID rid
) const
111 const SearchBox
* search_box_
;
114 SearchBoxIconURLHelper::SearchBoxIconURLHelper(const SearchBox
* search_box
)
115 : search_box_(search_box
) {
118 SearchBoxIconURLHelper::~SearchBoxIconURLHelper() {
121 int SearchBoxIconURLHelper::GetViewID() const {
122 return search_box_
->render_view()->GetRoutingID();
125 std::string
SearchBoxIconURLHelper::GetURLStringFromRestrictedID(
126 InstantRestrictedID rid
) const {
127 InstantMostVisitedItem item
;
128 if (!search_box_
->GetMostVisitedItemWithID(rid
, &item
))
129 return std::string();
131 return item
.url
.spec();
136 namespace internal
{ // for testing
138 // Parses "<view_id>/<restricted_id>". If successful, assigns
139 // |*view_id| := "<view_id>", |*rid| := "<restricted_id>", and returns true.
140 bool ParseViewIdAndRestrictedId(const std::string id_part
,
142 InstantRestrictedID
* rid_out
) {
145 // Check that the path is of Most visited item ID form.
146 std::vector
<std::string
> tokens
;
147 if (Tokenize(id_part
, "/", &tokens
) != 2)
151 InstantRestrictedID rid
;
152 if (!base::StringToInt(tokens
[0], &view_id
) || view_id
< 0 ||
153 !base::StringToInt(tokens
[1], &rid
) || rid
< 0)
156 *view_id_out
= view_id
;
161 // Takes icon |url| of given |type|, e.g., FAVICON looking like
163 // chrome-search://favicon/<view_id>/<restricted_id>
164 // chrome-search://favicon/<parameters>/<view_id>/<restricted_id>
166 // If successful, assigns |*param_part| := "" or "<parameters>/" (note trailing
167 // slash), |*view_id| := "<view_id>", |*rid| := "rid", and returns true.
168 bool ParseIconRestrictedUrl(const GURL
& url
,
169 SearchBox::ImageSourceType type
,
170 std::string
* param_part
,
172 InstantRestrictedID
* rid
) {
176 // Strip leading slash.
177 std::string raw_path
= url
.path();
178 DCHECK_GT(raw_path
.length(), (size_t) 0);
179 DCHECK_EQ(raw_path
[0], '/');
180 raw_path
= raw_path
.substr(1);
182 int path_index
= GetImagePathStartOfPageURL(type
, raw_path
);
186 std::string id_part
= raw_path
.substr(path_index
);
187 if (!ParseViewIdAndRestrictedId(id_part
, view_id
, rid
))
190 *param_part
= raw_path
.substr(0, path_index
);
194 bool TranslateIconRestrictedUrl(const GURL
& transient_url
,
195 SearchBox::ImageSourceType type
,
196 const SearchBox::IconURLHelper
& helper
,
200 InstantRestrictedID rid
= -1;
202 if (!internal::ParseIconRestrictedUrl(
203 transient_url
, type
, ¶ms
, &view_id
, &rid
) ||
204 view_id
!= helper
.GetViewID()) {
205 if (type
== SearchBox::FAVICON
) {
206 *url
= GURL(base::StringPrintf("chrome-search://%s/",
207 GetIconTypeUrlHost(SearchBox::FAVICON
)));
213 std::string item_url
= helper
.GetURLStringFromRestrictedID(rid
);
214 *url
= GURL(base::StringPrintf("chrome-search://%s/%s%s",
215 GetIconTypeUrlHost(type
),
221 } // namespace internal
223 SearchBox::IconURLHelper::IconURLHelper() {
226 SearchBox::IconURLHelper::~IconURLHelper() {
229 SearchBox::SearchBox(content::RenderView
* render_view
)
230 : content::RenderViewObserver(render_view
),
231 content::RenderViewObserverTracker
<SearchBox
>(render_view
),
233 app_launcher_enabled_(false),
235 is_input_in_progress_(false),
236 is_key_capture_enabled_(false),
237 display_instant_results_(false),
238 most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize
),
243 SearchBox::~SearchBox() {
246 void SearchBox::LogEvent(NTPLoggingEventType event
) {
247 // The main frame for the current RenderView may be out-of-process, in which
248 // case it won't have performance(). Use the default delta of 0 in this
250 base::TimeDelta delta
;
251 if (render_view()->GetWebView()->mainFrame()->isWebLocalFrame()) {
252 // navigation_start in ms.
253 uint64 start
= 1000 * (render_view()->GetMainRenderFrame()->GetWebFrame()->
254 performance().navigationStart());
255 uint64 now
= (base::TimeTicks::Now() - base::TimeTicks::UnixEpoch())
257 DCHECK(now
>= start
);
258 delta
= base::TimeDelta::FromMilliseconds(now
- start
);
260 render_view()->Send(new ChromeViewHostMsg_LogEvent(
261 render_view()->GetRoutingID(), page_seq_no_
, event
, delta
));
264 void SearchBox::LogMostVisitedImpression(int position
,
265 const base::string16
& provider
) {
266 render_view()->Send(new ChromeViewHostMsg_LogMostVisitedImpression(
267 render_view()->GetRoutingID(), page_seq_no_
, position
, provider
));
270 void SearchBox::LogMostVisitedNavigation(int position
,
271 const base::string16
& provider
) {
272 render_view()->Send(new ChromeViewHostMsg_LogMostVisitedNavigation(
273 render_view()->GetRoutingID(), page_seq_no_
, position
, provider
));
276 void SearchBox::CheckIsUserSignedInToChromeAs(const base::string16
& identity
) {
277 render_view()->Send(new ChromeViewHostMsg_ChromeIdentityCheck(
278 render_view()->GetRoutingID(), page_seq_no_
, identity
));
281 void SearchBox::CheckIsUserSyncingHistory() {
282 render_view()->Send(new ChromeViewHostMsg_HistorySyncCheck(
283 render_view()->GetRoutingID(), page_seq_no_
));
286 void SearchBox::DeleteMostVisitedItem(
287 InstantRestrictedID most_visited_item_id
) {
288 render_view()->Send(new ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(
289 render_view()->GetRoutingID(),
291 GetURLForMostVisitedItem(most_visited_item_id
)));
294 bool SearchBox::GenerateImageURLFromTransientURL(const GURL
& transient_url
,
295 ImageSourceType type
,
297 SearchBoxIconURLHelper
helper(this);
298 return internal::TranslateIconRestrictedUrl(transient_url
, type
, helper
, url
);
301 void SearchBox::GetMostVisitedItems(
302 std::vector
<InstantMostVisitedItemIDPair
>* items
) const {
303 return most_visited_items_cache_
.GetCurrentItems(items
);
306 bool SearchBox::GetMostVisitedItemWithID(
307 InstantRestrictedID most_visited_item_id
,
308 InstantMostVisitedItem
* item
) const {
309 return most_visited_items_cache_
.GetItemWithRestrictedID(most_visited_item_id
,
313 const ThemeBackgroundInfo
& SearchBox::GetThemeBackgroundInfo() {
317 const EmbeddedSearchRequestParams
& SearchBox::GetEmbeddedSearchRequestParams() {
318 return embedded_search_request_params_
;
321 void SearchBox::Focus() {
322 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
323 render_view()->GetRoutingID(), page_seq_no_
, OMNIBOX_FOCUS_VISIBLE
));
326 void SearchBox::NavigateToURL(const GURL
& url
,
327 WindowOpenDisposition disposition
,
328 bool is_most_visited_item_url
) {
329 render_view()->Send(new ChromeViewHostMsg_SearchBoxNavigate(
330 render_view()->GetRoutingID(), page_seq_no_
, url
,
331 disposition
, is_most_visited_item_url
));
334 void SearchBox::Paste(const base::string16
& text
) {
335 render_view()->Send(new ChromeViewHostMsg_PasteAndOpenDropdown(
336 render_view()->GetRoutingID(), page_seq_no_
, text
));
339 void SearchBox::SetVoiceSearchSupported(bool supported
) {
340 render_view()->Send(new ChromeViewHostMsg_SetVoiceSearchSupported(
341 render_view()->GetRoutingID(), page_seq_no_
, supported
));
344 void SearchBox::StartCapturingKeyStrokes() {
345 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
346 render_view()->GetRoutingID(), page_seq_no_
, OMNIBOX_FOCUS_INVISIBLE
));
349 void SearchBox::StopCapturingKeyStrokes() {
350 render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
351 render_view()->GetRoutingID(), page_seq_no_
, OMNIBOX_FOCUS_NONE
));
354 void SearchBox::UndoAllMostVisitedDeletions() {
356 new ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions(
357 render_view()->GetRoutingID(), page_seq_no_
));
360 void SearchBox::UndoMostVisitedDeletion(
361 InstantRestrictedID most_visited_item_id
) {
362 render_view()->Send(new ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(
363 render_view()->GetRoutingID(), page_seq_no_
,
364 GetURLForMostVisitedItem(most_visited_item_id
)));
367 bool SearchBox::OnMessageReceived(const IPC::Message
& message
) {
369 IPC_BEGIN_MESSAGE_MAP(SearchBox
, message
)
370 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetPageSequenceNumber
,
371 OnSetPageSequenceNumber
)
372 IPC_MESSAGE_HANDLER(ChromeViewMsg_ChromeIdentityCheckResult
,
373 OnChromeIdentityCheckResult
)
374 IPC_MESSAGE_HANDLER(ChromeViewMsg_DetermineIfPageSupportsInstant
,
375 OnDetermineIfPageSupportsInstant
)
376 IPC_MESSAGE_HANDLER(ChromeViewMsg_HistorySyncCheckResult
,
377 OnHistorySyncCheckResult
)
378 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged
, OnFocusChanged
)
379 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMarginChange
, OnMarginChange
)
380 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged
,
381 OnMostVisitedChanged
)
382 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation
,
383 OnPromoInformationReceived
)
384 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults
,
385 OnSetDisplayInstantResults
)
386 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress
,
387 OnSetInputInProgress
)
388 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch
,
389 OnSetSuggestionToPrefetch
)
390 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit
, OnSubmit
)
391 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged
,
393 IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxToggleVoiceSearch
,
395 IPC_MESSAGE_UNHANDLED(handled
= false)
396 IPC_END_MESSAGE_MAP()
400 void SearchBox::OnSetPageSequenceNumber(int page_seq_no
) {
401 page_seq_no_
= page_seq_no
;
404 void SearchBox::OnChromeIdentityCheckResult(const base::string16
& identity
,
405 bool identity_match
) {
406 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
407 extensions_v8::SearchBoxExtension::DispatchChromeIdentityCheckResult(
408 render_view()->GetWebView()->mainFrame(), identity
, identity_match
);
412 void SearchBox::OnDetermineIfPageSupportsInstant() {
413 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
414 bool result
= extensions_v8::SearchBoxExtension::PageSupportsInstant(
415 render_view()->GetWebView()->mainFrame());
416 DVLOG(1) << render_view() << " PageSupportsInstant: " << result
;
417 render_view()->Send(new ChromeViewHostMsg_InstantSupportDetermined(
418 render_view()->GetRoutingID(), page_seq_no_
, result
));
422 void SearchBox::OnFocusChanged(OmniboxFocusState new_focus_state
,
423 OmniboxFocusChangeReason reason
) {
424 bool key_capture_enabled
= new_focus_state
== OMNIBOX_FOCUS_INVISIBLE
;
425 if (key_capture_enabled
!= is_key_capture_enabled_
) {
426 // Tell the page if the key capture mode changed unless the focus state
427 // changed because of TYPING. This is because in that case, the browser
428 // hasn't really stopped capturing key strokes.
430 // (More practically, if we don't do this check, the page would receive
431 // onkeycapturechange before the corresponding onchange, and the page would
432 // have no way of telling whether the keycapturechange happened because of
433 // some actual user action or just because they started typing.)
434 if (reason
!= OMNIBOX_FOCUS_CHANGE_TYPING
&&
435 render_view()->GetWebView() &&
436 render_view()->GetWebView()->mainFrame()) {
437 is_key_capture_enabled_
= key_capture_enabled
;
438 DVLOG(1) << render_view() << " OnKeyCaptureChange";
439 extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
440 render_view()->GetWebView()->mainFrame());
443 bool is_focused
= new_focus_state
== OMNIBOX_FOCUS_VISIBLE
;
444 if (is_focused
!= is_focused_
) {
445 is_focused_
= is_focused
;
446 DVLOG(1) << render_view() << " OnFocusChange";
447 if (render_view()->GetWebView() &&
448 render_view()->GetWebView()->mainFrame()) {
449 extensions_v8::SearchBoxExtension::DispatchFocusChange(
450 render_view()->GetWebView()->mainFrame());
455 void SearchBox::OnHistorySyncCheckResult(bool sync_history
) {
456 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
457 extensions_v8::SearchBoxExtension::DispatchHistorySyncCheckResult(
458 render_view()->GetWebView()->mainFrame(), sync_history
);
462 void SearchBox::OnMarginChange(int margin
) {
463 start_margin_
= margin
;
464 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
465 extensions_v8::SearchBoxExtension::DispatchMarginChange(
466 render_view()->GetWebView()->mainFrame());
470 void SearchBox::OnMostVisitedChanged(
471 const std::vector
<InstantMostVisitedItem
>& items
) {
472 std::vector
<InstantMostVisitedItemIDPair
> last_known_items
;
473 GetMostVisitedItems(&last_known_items
);
475 if (AreMostVisitedItemsEqual(last_known_items
, items
))
476 return; // Do not send duplicate onmostvisitedchange events.
478 most_visited_items_cache_
.AddItems(items
);
479 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
480 extensions_v8::SearchBoxExtension::DispatchMostVisitedChanged(
481 render_view()->GetWebView()->mainFrame());
485 void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled
) {
486 app_launcher_enabled_
= is_app_launcher_enabled
;
489 void SearchBox::OnSetDisplayInstantResults(bool display_instant_results
) {
490 display_instant_results_
= display_instant_results
;
493 void SearchBox::OnSetInputInProgress(bool is_input_in_progress
) {
494 if (is_input_in_progress_
!= is_input_in_progress
) {
495 is_input_in_progress_
= is_input_in_progress
;
496 DVLOG(1) << render_view() << " OnSetInputInProgress";
497 if (render_view()->GetWebView() &&
498 render_view()->GetWebView()->mainFrame()) {
499 if (is_input_in_progress_
) {
500 extensions_v8::SearchBoxExtension::DispatchInputStart(
501 render_view()->GetWebView()->mainFrame());
503 extensions_v8::SearchBoxExtension::DispatchInputCancel(
504 render_view()->GetWebView()->mainFrame());
510 void SearchBox::OnSetSuggestionToPrefetch(const InstantSuggestion
& suggestion
) {
511 suggestion_
= suggestion
;
512 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
513 DVLOG(1) << render_view() << " OnSetSuggestionToPrefetch";
514 extensions_v8::SearchBoxExtension::DispatchSuggestionChange(
515 render_view()->GetWebView()->mainFrame());
519 void SearchBox::OnSubmit(const base::string16
& query
,
520 const EmbeddedSearchRequestParams
& params
) {
522 embedded_search_request_params_
= params
;
523 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
524 DVLOG(1) << render_view() << " OnSubmit";
525 extensions_v8::SearchBoxExtension::DispatchSubmit(
526 render_view()->GetWebView()->mainFrame());
532 void SearchBox::OnThemeChanged(const ThemeBackgroundInfo
& theme_info
) {
533 // Do not send duplicate notifications.
534 if (theme_info_
== theme_info
)
537 theme_info_
= theme_info
;
538 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
539 extensions_v8::SearchBoxExtension::DispatchThemeChange(
540 render_view()->GetWebView()->mainFrame());
544 void SearchBox::OnToggleVoiceSearch() {
545 if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
546 extensions_v8::SearchBoxExtension::DispatchToggleVoiceSearch(
547 render_view()->GetWebView()->mainFrame());
551 GURL
SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id
) const {
552 InstantMostVisitedItem item
;
553 return GetMostVisitedItemWithID(item_id
, &item
) ? item
.url
: GURL();
556 void SearchBox::Reset() {
558 embedded_search_request_params_
= EmbeddedSearchRequestParams();
559 suggestion_
= InstantSuggestion();
562 is_key_capture_enabled_
= false;
563 theme_info_
= ThemeBackgroundInfo();