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
17 int FindTabHelper::find_request_id_counter_ = -1;
18
FindTabHelper(TabContents * tab_contents)19 FindTabHelper::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
~FindTabHelper()28 FindTabHelper::~FindTabHelper() {
29 }
30
StartFinding(string16 search_string,bool forward_direction,bool case_sensitive)31 void 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
StopFinding(FindBarController::SelectionAction selection_action)81 void 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
OnMessageReceived(const IPC::Message & message)99 bool 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
OnFindReply(int request_id,int number_of_matches,const gfx::Rect & selection_rect,int active_match_ordinal,bool final_update)108 void 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