18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Copyright (c) 2012 The Chromium Authors. All rights reserved. 28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Use of this source code is governed by a BSD-style license that can be 38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// found in the LICENSE file. 48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_tab_helper.h" 6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt 78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <vector> 88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/chrome_notification_types.h" 108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/profiles/profile.h" 118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_bar_state.h" 128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_bar_state_factory.h" 138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/notification_service.h" 148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/render_view_host.h" 158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/web_contents.h" 168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/common/stop_find_action.h" 178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "third_party/WebKit/public/web/WebFindOptions.h" 188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "ui/gfx/rect_f.h" 198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing blink::WebFindOptions; 218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing content::WebContents; 228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 238d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtDEFINE_WEB_CONTENTS_USER_DATA_KEY(FindTabHelper); 248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// static 268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FindTabHelper::find_request_id_counter_ = -1; 278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 288d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtFindTabHelper::FindTabHelper(WebContents* web_contents) 298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt : content::WebContentsObserver(web_contents), 308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_ui_active_(false), 318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_op_aborted_(false), 328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt current_find_request_id_(find_request_id_counter_++), 338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt last_search_case_sensitive_(false), 348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt last_search_result_() { 358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 378d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtFindTabHelper::~FindTabHelper() { 388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::StartFinding(base::string16 search_string, 418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool forward_direction, 428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool case_sensitive) { 438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Remove the carriage return character, which generally isn't in web content. 448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const base::char16 kInvalidChars[] = { '\r', 0 }; 458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt base::RemoveChars(search_string, kInvalidChars, &search_string); 468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // If search_string is empty, it means FindNext was pressed with a keyboard 488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // shortcut so unless we have something to search for we return early. 498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (search_string.empty() && find_text_.empty()) { 508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Profile* profile = 518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt base::string16 last_search_prepopulate_text = 538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt FindBarStateFactory::GetLastPrepopulateText(profile); 548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Try the last thing we searched for on this tab, then the last thing 568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // searched for on any tab. 578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!previous_find_text_.empty()) 588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt search_string = previous_find_text_; 598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt else if (!last_search_prepopulate_text.empty()) 608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt search_string = last_search_prepopulate_text; 618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt else 628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return; 638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Keep track of the previous search. 668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt previous_find_text_ = find_text_; 678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // This is a FindNext operation if we are searching for the same text again, 698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // or if the passed in search text is empty (FindNext keyboard shortcut). The 708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // exception to this is if the Find was aborted (then we don't want FindNext 718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // because the highlighting has been cleared and we need it to reappear). We 728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // therefore treat FindNext after an aborted Find operation as a full fledged 738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Find. 748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool find_next = (find_text_ == search_string || search_string.empty()) && 758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt (last_search_case_sensitive_ == case_sensitive) && 768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt !find_op_aborted_; 778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!find_next) 788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt current_find_request_id_ = find_request_id_counter_++; 798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!search_string.empty()) 818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_text_ = search_string; 828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt last_search_case_sensitive_ = case_sensitive; 838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_op_aborted_ = false; 858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Keep track of what the last search was across the tabs. 878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Profile* profile = 888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt FindBarState* find_bar_state = FindBarStateFactory::GetForProfile(profile); 908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_bar_state->set_last_prepopulate_text(find_text_); 918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt WebFindOptions options; 938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt options.forward = forward_direction; 948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt options.matchCase = case_sensitive; 958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt options.findNext = find_next; 968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt web_contents()->Find(current_find_request_id_, find_text_, options); 978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::StopFinding( 1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt FindBarController::SelectionAction selection_action) { 1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (selection_action == FindBarController::kClearSelectionOnPage) { 1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // kClearSelection means the find string has been cleared by the user, but 1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // the UI has not been dismissed. In that case we want to clear the 1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // previously remembered search (http://crbug.com/42639). 1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt previous_find_text_ = base::string16(); 1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } else { 1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_ui_active_ = false; 1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!find_text_.empty()) 1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt previous_find_text_ = find_text_; 1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_text_.clear(); 1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt find_op_aborted_ = true; 1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt last_search_result_ = FindNotificationDetails(); 1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt content::StopFindAction action; 1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt switch (selection_action) { 1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case FindBarController::kClearSelectionOnPage: 1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt action = content::STOP_FIND_ACTION_CLEAR_SELECTION; 1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case FindBarController::kKeepSelectionOnPage: 1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt action = content::STOP_FIND_ACTION_KEEP_SELECTION; 1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case FindBarController::kActivateSelectionOnPage: 1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION; 1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt default: 1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt NOTREACHED(); 1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt action = content::STOP_FIND_ACTION_KEEP_SELECTION; 1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt web_contents()->StopFinding(action); 1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#if defined(OS_ANDROID) 1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::ActivateNearestFindResult(float x, float y) { 1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!find_op_aborted_ && !find_text_.empty()) { 1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt web_contents()->GetRenderViewHost()->ActivateNearestFindResult( 1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt current_find_request_id_, x, y); 1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::RequestFindMatchRects(int current_version) { 1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!find_op_aborted_ && !find_text_.empty()) 1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt web_contents()->GetRenderViewHost()->RequestFindMatchRects(current_version); 1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#endif 1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::HandleFindReply(int request_id, 1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int number_of_matches, 1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt const gfx::Rect& selection_rect, 1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int active_match_ordinal, 1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt bool final_update) { 1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Ignore responses for requests that have been aborted. 1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Ignore responses for requests other than the one we have most recently 1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // issued. That way we won't act on stale results when the user has 1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // already typed in another query. 1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!find_op_aborted_ && request_id == current_find_request_id_) { 1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (number_of_matches == -1) 1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt number_of_matches = last_search_result_.number_of_matches(); 1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (active_match_ordinal == -1) 1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt active_match_ordinal = last_search_result_.active_match_ordinal(); 1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt gfx::Rect selection = selection_rect; 1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (final_update && active_match_ordinal == 0) 1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt selection = gfx::Rect(); 1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt else if (selection_rect.IsEmpty()) 1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt selection = last_search_result_.selection_rect(); 1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // Notify the UI, automation and any other observers that a find result was 1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt // found. 1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt last_search_result_ = FindNotificationDetails( 1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt request_id, number_of_matches, selection, active_match_ordinal, 1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt final_update); 1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt content::NotificationService::current()->Notify( 1744530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, 1754530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt content::Source<WebContents>(web_contents()), 1764530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt content::Details<FindNotificationDetails>(&last_search_result_)); 1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt