1// Copyright (c) 2011 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/browser/ui/find_bar/find_tab_helper.h" 6 7#include <vector> 8 9#include "chrome/browser/profiles/profile.h" 10#include "chrome/browser/ui/find_bar/find_bar_state.h" 11#include "content/browser/renderer_host/render_view_host.h" 12#include "content/browser/tab_contents/tab_contents.h" 13#include "content/common/notification_service.h" 14#include "content/common/view_messages.h" 15 16// static 17int FindTabHelper::find_request_id_counter_ = -1; 18 19FindTabHelper::FindTabHelper(TabContents* tab_contents) 20 : TabContentsObserver(tab_contents), 21 find_ui_active_(false), 22 find_op_aborted_(false), 23 current_find_request_id_(find_request_id_counter_++), 24 last_search_case_sensitive_(false), 25 last_search_result_() { 26} 27 28FindTabHelper::~FindTabHelper() { 29} 30 31void FindTabHelper::StartFinding(string16 search_string, 32 bool forward_direction, 33 bool case_sensitive) { 34 // If search_string is empty, it means FindNext was pressed with a keyboard 35 // shortcut so unless we have something to search for we return early. 36 if (search_string.empty() && find_text_.empty()) { 37 string16 last_search_prepopulate_text = 38 FindBarState::GetLastPrepopulateText(tab_contents()->profile()); 39 40 // Try the last thing we searched for on this tab, then the last thing 41 // searched for on any tab. 42 if (!previous_find_text_.empty()) 43 search_string = previous_find_text_; 44 else if (!last_search_prepopulate_text.empty()) 45 search_string = last_search_prepopulate_text; 46 else 47 return; 48 } 49 50 // Keep track of the previous search. 51 previous_find_text_ = find_text_; 52 53 // This is a FindNext operation if we are searching for the same text again, 54 // or if the passed in search text is empty (FindNext keyboard shortcut). The 55 // exception to this is if the Find was aborted (then we don't want FindNext 56 // because the highlighting has been cleared and we need it to reappear). We 57 // therefore treat FindNext after an aborted Find operation as a full fledged 58 // Find. 59 bool find_next = (find_text_ == search_string || search_string.empty()) && 60 (last_search_case_sensitive_ == case_sensitive) && 61 !find_op_aborted_; 62 if (!find_next) 63 current_find_request_id_ = find_request_id_counter_++; 64 65 if (!search_string.empty()) 66 find_text_ = search_string; 67 last_search_case_sensitive_ = case_sensitive; 68 69 find_op_aborted_ = false; 70 71 // Keep track of what the last search was across the tabs. 72 FindBarState* find_bar_state = tab_contents()->profile()->GetFindBarState(); 73 find_bar_state->set_last_prepopulate_text(find_text_); 74 tab_contents()->render_view_host()->StartFinding(current_find_request_id_, 75 find_text_, 76 forward_direction, 77 case_sensitive, 78 find_next); 79} 80 81void FindTabHelper::StopFinding( 82 FindBarController::SelectionAction selection_action) { 83 if (selection_action == FindBarController::kClearSelection) { 84 // kClearSelection means the find string has been cleared by the user, but 85 // the UI has not been dismissed. In that case we want to clear the 86 // previously remembered search (http://crbug.com/42639). 87 previous_find_text_ = string16(); 88 } else { 89 find_ui_active_ = false; 90 if (!find_text_.empty()) 91 previous_find_text_ = find_text_; 92 } 93 find_text_.clear(); 94 find_op_aborted_ = true; 95 last_search_result_ = FindNotificationDetails(); 96 tab_contents()->render_view_host()->StopFinding(selection_action); 97} 98 99bool FindTabHelper::OnMessageReceived(const IPC::Message& message) { 100 bool handled = true; 101 IPC_BEGIN_MESSAGE_MAP(FindTabHelper, message) 102 IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnFindReply) 103 IPC_MESSAGE_UNHANDLED(handled = false) 104 IPC_END_MESSAGE_MAP() 105 return handled; 106} 107 108void FindTabHelper::OnFindReply(int request_id, 109 int number_of_matches, 110 const gfx::Rect& selection_rect, 111 int active_match_ordinal, 112 bool final_update) { 113 // Ignore responses for requests that have been aborted. 114 // Ignore responses for requests other than the one we have most recently 115 // issued. That way we won't act on stale results when the user has 116 // already typed in another query. 117 if (!find_op_aborted_ && request_id == current_find_request_id_) { 118 if (number_of_matches == -1) 119 number_of_matches = last_search_result_.number_of_matches(); 120 if (active_match_ordinal == -1) 121 active_match_ordinal = last_search_result_.active_match_ordinal(); 122 123 gfx::Rect selection = selection_rect; 124 if (selection.IsEmpty()) 125 selection = last_search_result_.selection_rect(); 126 127 // Notify the UI, automation and any other observers that a find result was 128 // found. 129 last_search_result_ = FindNotificationDetails( 130 request_id, number_of_matches, selection, active_match_ordinal, 131 final_update); 132 NotificationService::current()->Notify( 133 NotificationType::FIND_RESULT_AVAILABLE, 134 Source<TabContents>(tab_contents()), 135 Details<FindNotificationDetails>(&last_search_result_)); 136 } 137 138 // Send a notification to the renderer that we are ready to receive more 139 // results from the scoping effort of the Find operation. The FindInPage 140 // scoping is asynchronous and periodically sends results back up to the 141 // browser using IPC. In an effort to not spam the browser we have the 142 // browser send an ACK for each FindReply message and have the renderer 143 // queue up the latest status message while waiting for this ACK. 144 Send(new ViewMsg_FindReplyACK(routing_id())); 145} 146