1 // Copyright (c) 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/browser/ui/find_bar/find_tab_helper.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/find_bar/find_bar_state.h"
12 #include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/common/stop_find_action.h"
18 #include "third_party/WebKit/public/web/WebFindOptions.h"
19 #include "ui/gfx/geometry/rect_f.h"
21 using blink::WebFindOptions
;
22 using content::WebContents
;
24 DEFINE_WEB_CONTENTS_USER_DATA_KEY(FindTabHelper
);
27 int FindTabHelper::find_request_id_counter_
= -1;
29 FindTabHelper::FindTabHelper(WebContents
* web_contents
)
30 : content::WebContentsObserver(web_contents
),
31 find_ui_active_(false),
32 find_op_aborted_(false),
33 current_find_request_id_(find_request_id_counter_
++),
34 last_search_case_sensitive_(false),
35 last_search_result_() {
38 FindTabHelper::~FindTabHelper() {
41 void FindTabHelper::StartFinding(base::string16 search_string
,
42 bool forward_direction
,
43 bool case_sensitive
) {
44 // Remove the carriage return character, which generally isn't in web content.
45 const base::char16 kInvalidChars
[] = { '\r', 0 };
46 base::RemoveChars(search_string
, kInvalidChars
, &search_string
);
48 // If search_string is empty, it means FindNext was pressed with a keyboard
49 // shortcut so unless we have something to search for we return early.
50 if (search_string
.empty() && find_text_
.empty()) {
52 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
53 base::string16 last_search_prepopulate_text
=
54 FindBarStateFactory::GetLastPrepopulateText(profile
);
56 // Try the last thing we searched for on this tab, then the last thing
57 // searched for on any tab.
58 if (!previous_find_text_
.empty())
59 search_string
= previous_find_text_
;
60 else if (!last_search_prepopulate_text
.empty())
61 search_string
= last_search_prepopulate_text
;
66 // Keep track of the previous search.
67 previous_find_text_
= find_text_
;
69 // This is a FindNext operation if we are searching for the same text again,
70 // or if the passed in search text is empty (FindNext keyboard shortcut). The
71 // exception to this is if the Find was aborted (then we don't want FindNext
72 // because the highlighting has been cleared and we need it to reappear). We
73 // therefore treat FindNext after an aborted Find operation as a full fledged
75 bool find_next
= (find_text_
== search_string
|| search_string
.empty()) &&
76 (last_search_case_sensitive_
== case_sensitive
) &&
79 current_find_request_id_
= find_request_id_counter_
++;
81 if (!search_string
.empty())
82 find_text_
= search_string
;
83 last_search_case_sensitive_
= case_sensitive
;
85 find_op_aborted_
= false;
87 // Keep track of what the last search was across the tabs.
89 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
90 FindBarState
* find_bar_state
= FindBarStateFactory::GetForProfile(profile
);
91 find_bar_state
->set_last_prepopulate_text(find_text_
);
93 WebFindOptions options
;
94 options
.forward
= forward_direction
;
95 options
.matchCase
= case_sensitive
;
96 options
.findNext
= find_next
;
97 web_contents()->Find(current_find_request_id_
, find_text_
, options
);
100 void FindTabHelper::StopFinding(
101 FindBarController::SelectionAction selection_action
) {
102 if (selection_action
== FindBarController::kClearSelectionOnPage
) {
103 // kClearSelection means the find string has been cleared by the user, but
104 // the UI has not been dismissed. In that case we want to clear the
105 // previously remembered search (http://crbug.com/42639).
106 previous_find_text_
= base::string16();
108 find_ui_active_
= false;
109 if (!find_text_
.empty())
110 previous_find_text_
= find_text_
;
113 find_op_aborted_
= true;
114 last_search_result_
= FindNotificationDetails();
116 content::StopFindAction action
;
117 switch (selection_action
) {
118 case FindBarController::kClearSelectionOnPage
:
119 action
= content::STOP_FIND_ACTION_CLEAR_SELECTION
;
121 case FindBarController::kKeepSelectionOnPage
:
122 action
= content::STOP_FIND_ACTION_KEEP_SELECTION
;
124 case FindBarController::kActivateSelectionOnPage
:
125 action
= content::STOP_FIND_ACTION_ACTIVATE_SELECTION
;
129 action
= content::STOP_FIND_ACTION_KEEP_SELECTION
;
131 web_contents()->StopFinding(action
);
134 void FindTabHelper::ActivateFindInPageResultForAccessibility() {
135 web_contents()->GetMainFrame()->ActivateFindInPageResultForAccessibility(
136 current_find_request_id_
);
139 #if defined(OS_ANDROID)
140 void FindTabHelper::ActivateNearestFindResult(float x
, float y
) {
141 if (!find_op_aborted_
&& !find_text_
.empty()) {
142 web_contents()->GetRenderViewHost()->ActivateNearestFindResult(
143 current_find_request_id_
, x
, y
);
147 void FindTabHelper::RequestFindMatchRects(int current_version
) {
148 if (!find_op_aborted_
&& !find_text_
.empty())
149 web_contents()->GetRenderViewHost()->RequestFindMatchRects(current_version
);
153 void FindTabHelper::HandleFindReply(int request_id
,
154 int number_of_matches
,
155 const gfx::Rect
& selection_rect
,
156 int active_match_ordinal
,
158 // Ignore responses for requests that have been aborted.
159 // Ignore responses for requests other than the one we have most recently
160 // issued. That way we won't act on stale results when the user has
161 // already typed in another query.
162 if (!find_op_aborted_
&& request_id
== current_find_request_id_
) {
163 if (number_of_matches
== -1)
164 number_of_matches
= last_search_result_
.number_of_matches();
165 if (active_match_ordinal
== -1)
166 active_match_ordinal
= last_search_result_
.active_match_ordinal();
168 gfx::Rect selection
= selection_rect
;
169 if (final_update
&& active_match_ordinal
== 0)
170 selection
= gfx::Rect();
171 else if (selection_rect
.IsEmpty())
172 selection
= last_search_result_
.selection_rect();
174 // Notify the UI, automation and any other observers that a find result was
176 last_search_result_
= FindNotificationDetails(
177 request_id
, number_of_matches
, selection
, active_match_ordinal
,
179 content::NotificationService::current()->Notify(
180 chrome::NOTIFICATION_FIND_RESULT_AVAILABLE
,
181 content::Source
<WebContents
>(web_contents()),
182 content::Details
<FindNotificationDetails
>(&last_search_result_
));