blob: 72bc610e2a77c3f975be5c3df7c87572cc029ab8 [file] [log] [blame]
[email protected]e9856c22012-01-27 01:51:591// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]9c318862011-02-01 22:27:242// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]c90c6ca2011-02-16 20:11:385#include "chrome/browser/ui/find_bar/find_tab_helper.h"
[email protected]9c318862011-02-01 22:27:246
Eric Lawrence92b29bf2019-02-15 16:40:247#include <utility>
[email protected]9c318862011-02-01 22:27:248#include <vector>
9
avif9ab5d942015-10-15 14:05:4410#include "base/strings/string_util.h"
avi655876a2015-12-25 07:18:1511#include "build/build_config.h"
[email protected]1a7ff742013-07-12 00:26:2312#include "chrome/browser/chrome_notification_types.h"
[email protected]9c318862011-02-01 22:27:2413#include "chrome/browser/profiles/profile.h"
[email protected]9c318862011-02-01 22:27:2414#include "chrome/browser/ui/find_bar/find_bar_state.h"
[email protected]e9856c22012-01-27 01:51:5915#include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
Evan Stade70a3cef2019-07-11 20:45:3616#include "chrome/browser/ui/find_bar/find_result_observer.h"
17#include "chrome/browser/ui/find_bar/find_types.h"
[email protected]ad50def52011-10-19 23:17:0718#include "content/public/browser/notification_service.h"
dmazzoni1a69e2b32014-11-06 20:34:2819#include "content/public/browser/render_frame_host.h"
[email protected]9c1662b2012-03-06 15:44:3320#include "content/public/browser/render_view_host.h"
[email protected]d9083482012-01-06 00:38:4621#include "content/public/browser/web_contents.h"
[email protected]815dd9872011-11-23 18:40:3022#include "content/public/common/stop_find_action.h"
Rakina Zata Amni3f77dff2018-09-08 16:19:4323#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
tfarina3b0452d2014-12-31 15:20:0924#include "ui/gfx/geometry/rect_f.h"
[email protected]216813952011-05-19 22:21:2625
[email protected]768c5472011-12-26 19:06:1726using content::WebContents;
[email protected]9c318862011-02-01 22:27:2427
28// static
[email protected]c90c6ca2011-02-16 20:11:3829int FindTabHelper::find_request_id_counter_ = -1;
[email protected]9c318862011-02-01 22:27:2430
[email protected]d9083482012-01-06 00:38:4631FindTabHelper::FindTabHelper(WebContents* web_contents)
32 : content::WebContentsObserver(web_contents),
[email protected]9c318862011-02-01 22:27:2433 find_ui_active_(false),
34 find_op_aborted_(false),
35 current_find_request_id_(find_request_id_counter_++),
paulmeyer1cfca292016-04-25 23:25:5736 current_find_session_id_(current_find_request_id_),
[email protected]9c318862011-02-01 22:27:2437 last_search_case_sensitive_(false),
38 last_search_result_() {
[email protected]9c318862011-02-01 22:27:2439}
40
Evan Stade0e39ab762019-11-15 01:42:1341FindTabHelper::~FindTabHelper() {
42 for (auto& observer : observers_)
43 observer.OnFindTabHelperDestroyed(this);
44}
Evan Stade70a3cef2019-07-11 20:45:3645
46void FindTabHelper::AddObserver(FindResultObserver* observer) {
47 observers_.AddObserver(observer);
48}
49
50void FindTabHelper::RemoveObserver(FindResultObserver* observer) {
51 observers_.RemoveObserver(observer);
[email protected]9c318862011-02-01 22:27:2452}
53
[email protected]b959d7d42013-12-13 17:26:3754void FindTabHelper::StartFinding(base::string16 search_string,
[email protected]c90c6ca2011-02-16 20:11:3855 bool forward_direction,
Rakina Zata Amnid4ce97f2018-08-17 12:51:4256 bool case_sensitive,
57 bool run_synchronously_for_testing) {
[email protected]102402c2014-07-09 19:53:3858 // Remove the carriage return character, which generally isn't in web content.
59 const base::char16 kInvalidChars[] = { '\r', 0 };
60 base::RemoveChars(search_string, kInvalidChars, &search_string);
61
[email protected]9c318862011-02-01 22:27:2462 // If search_string is empty, it means FindNext was pressed with a keyboard
63 // shortcut so unless we have something to search for we return early.
64 if (search_string.empty() && find_text_.empty()) {
[email protected]4c1865a2011-08-01 14:30:3965 Profile* profile =
[email protected]768c5472011-12-26 19:06:1766 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
[email protected]a04db822013-12-11 19:14:4067 base::string16 last_search_prepopulate_text =
[email protected]e9856c22012-01-27 01:51:5968 FindBarStateFactory::GetLastPrepopulateText(profile);
[email protected]9c318862011-02-01 22:27:2469
70 // Try the last thing we searched for on this tab, then the last thing
71 // searched for on any tab.
72 if (!previous_find_text_.empty())
73 search_string = previous_find_text_;
74 else if (!last_search_prepopulate_text.empty())
75 search_string = last_search_prepopulate_text;
76 else
77 return;
78 }
79
80 // Keep track of the previous search.
81 previous_find_text_ = find_text_;
82
83 // This is a FindNext operation if we are searching for the same text again,
84 // or if the passed in search text is empty (FindNext keyboard shortcut). The
85 // exception to this is if the Find was aborted (then we don't want FindNext
86 // because the highlighting has been cleared and we need it to reappear). We
87 // therefore treat FindNext after an aborted Find operation as a full fledged
88 // Find.
89 bool find_next = (find_text_ == search_string || search_string.empty()) &&
90 (last_search_case_sensitive_ == case_sensitive) &&
91 !find_op_aborted_;
paulmeyerc0b762b2016-04-13 11:55:1792
93 current_find_request_id_ = find_request_id_counter_++;
paulmeyer1cfca292016-04-25 23:25:5794 if (!find_next)
95 current_find_session_id_ = current_find_request_id_;
[email protected]9c318862011-02-01 22:27:2496
97 if (!search_string.empty())
98 find_text_ = search_string;
99 last_search_case_sensitive_ = case_sensitive;
100
101 find_op_aborted_ = false;
102
103 // Keep track of what the last search was across the tabs.
[email protected]4c1865a2011-08-01 14:30:39104 Profile* profile =
[email protected]768c5472011-12-26 19:06:17105 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
[email protected]e9856c22012-01-27 01:51:59106 FindBarState* find_bar_state = FindBarStateFactory::GetForProfile(profile);
[email protected]9c318862011-02-01 22:27:24107 find_bar_state->set_last_prepopulate_text(find_text_);
[email protected]216813952011-05-19 22:21:26108
Rakina Zata Amni3f77dff2018-09-08 16:19:43109 auto options = blink::mojom::FindOptions::New();
110 options->forward = forward_direction;
111 options->match_case = case_sensitive;
112 options->find_next = find_next;
113 options->run_synchronously_for_testing = run_synchronously_for_testing;
114 web_contents()->Find(current_find_request_id_, find_text_,
115 std::move(options));
[email protected]9c318862011-02-01 22:27:24116}
117
Evan Stade70a3cef2019-07-11 20:45:36118void FindTabHelper::StopFinding(FindOnPageSelectionAction selection_action) {
119 if (selection_action == FindOnPageSelectionAction::kClear) {
[email protected]9c318862011-02-01 22:27:24120 // kClearSelection means the find string has been cleared by the user, but
121 // the UI has not been dismissed. In that case we want to clear the
122 // previously remembered search (http://crbug.com/42639).
[email protected]a04db822013-12-11 19:14:40123 previous_find_text_ = base::string16();
[email protected]9c318862011-02-01 22:27:24124 } else {
125 find_ui_active_ = false;
126 if (!find_text_.empty())
127 previous_find_text_ = find_text_;
128 }
129 find_text_.clear();
Eric Lawrence92b29bf2019-02-15 16:40:24130 last_completed_find_text_.clear();
[email protected]9c318862011-02-01 22:27:24131 find_op_aborted_ = true;
132 last_search_result_ = FindNotificationDetails();
[email protected]216813952011-05-19 22:21:26133
[email protected]815dd9872011-11-23 18:40:30134 content::StopFindAction action;
[email protected]216813952011-05-19 22:21:26135 switch (selection_action) {
Evan Stade70a3cef2019-07-11 20:45:36136 case FindOnPageSelectionAction::kClear:
[email protected]815dd9872011-11-23 18:40:30137 action = content::STOP_FIND_ACTION_CLEAR_SELECTION;
[email protected]216813952011-05-19 22:21:26138 break;
Evan Stade70a3cef2019-07-11 20:45:36139 case FindOnPageSelectionAction::kKeep:
[email protected]815dd9872011-11-23 18:40:30140 action = content::STOP_FIND_ACTION_KEEP_SELECTION;
[email protected]216813952011-05-19 22:21:26141 break;
Evan Stade70a3cef2019-07-11 20:45:36142 case FindOnPageSelectionAction::kActivate:
[email protected]815dd9872011-11-23 18:40:30143 action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION;
[email protected]216813952011-05-19 22:21:26144 break;
145 default:
146 NOTREACHED();
[email protected]815dd9872011-11-23 18:40:30147 action = content::STOP_FIND_ACTION_KEEP_SELECTION;
[email protected]216813952011-05-19 22:21:26148 }
[email protected]36ec24f2014-01-09 00:32:08149 web_contents()->StopFinding(action);
[email protected]9c318862011-02-01 22:27:24150}
151
dmazzoni1a69e2b32014-11-06 20:34:28152void FindTabHelper::ActivateFindInPageResultForAccessibility() {
153 web_contents()->GetMainFrame()->ActivateFindInPageResultForAccessibility(
154 current_find_request_id_);
155}
156
[email protected]59363fc92012-09-05 03:46:31157#if defined(OS_ANDROID)
158void FindTabHelper::ActivateNearestFindResult(float x, float y) {
159 if (!find_op_aborted_ && !find_text_.empty()) {
paulmeyerc0b762b2016-04-13 11:55:17160 web_contents()->ActivateNearestFindResult(x, y);
[email protected]59363fc92012-09-05 03:46:31161 }
162}
163
164void FindTabHelper::RequestFindMatchRects(int current_version) {
165 if (!find_op_aborted_ && !find_text_.empty())
paulmeyerc0b762b2016-04-13 11:55:17166 web_contents()->RequestFindMatchRects(current_version);
[email protected]59363fc92012-09-05 03:46:31167}
168#endif
169
[email protected]b888919c2011-09-02 00:32:16170void FindTabHelper::HandleFindReply(int request_id,
171 int number_of_matches,
172 const gfx::Rect& selection_rect,
173 int active_match_ordinal,
174 bool final_update) {
[email protected]9c318862011-02-01 22:27:24175 // Ignore responses for requests that have been aborted.
paulmeyer1cfca292016-04-25 23:25:57176 // Ignore responses for requests from previous sessions. That way we won't act
177 // on stale results when the user has already typed in another query.
178 if (!find_op_aborted_ && request_id >= current_find_session_id_) {
[email protected]9c318862011-02-01 22:27:24179 if (number_of_matches == -1)
180 number_of_matches = last_search_result_.number_of_matches();
181 if (active_match_ordinal == -1)
182 active_match_ordinal = last_search_result_.active_match_ordinal();
183
184 gfx::Rect selection = selection_rect;
[email protected]27e7a0552012-03-16 00:01:17185 if (final_update && active_match_ordinal == 0)
186 selection = gfx::Rect();
187 else if (selection_rect.IsEmpty())
[email protected]9c318862011-02-01 22:27:24188 selection = last_search_result_.selection_rect();
189
190 // Notify the UI, automation and any other observers that a find result was
191 // found.
192 last_search_result_ = FindNotificationDetails(
193 request_id, number_of_matches, selection, active_match_ordinal,
194 final_update);
Evan Stade70a3cef2019-07-11 20:45:36195 for (auto& observer : observers_)
196 observer.OnFindResultAvailable(web_contents());
[email protected]9c318862011-02-01 22:27:24197 }
[email protected]9c318862011-02-01 22:27:24198}
François Doray4f51d5d2018-12-03 22:26:24199
200WEB_CONTENTS_USER_DATA_KEY_IMPL(FindTabHelper)