Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 5 | #include "components/find_in_page/find_tab_helper.h" |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 6 | |
Eric Lawrence | 92b29bf | 2019-02-15 16:40:24 | [diff] [blame] | 7 | #include <utility> |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 8 | |
David Sanders | 646a03e | 2022-02-22 14:33:34 | [diff] [blame] | 9 | #include "base/observer_list.h" |
avi | f9ab5d94 | 2015-10-15 14:05:44 | [diff] [blame] | 10 | #include "base/strings/string_util.h" |
avi | 655876a | 2015-12-25 07:18:15 | [diff] [blame] | 11 | #include "build/build_config.h" |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 12 | #include "components/find_in_page/find_result_observer.h" |
| 13 | #include "components/find_in_page/find_types.h" |
dmazzoni | 1a69e2b3 | 2014-11-06 20:34:28 | [diff] [blame] | 14 | #include "content/public/browser/render_frame_host.h" |
[email protected] | d908348 | 2012-01-06 00:38:46 | [diff] [blame] | 15 | #include "content/public/browser/web_contents.h" |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 16 | #include "content/public/common/stop_find_action.h" |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 17 | #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" |
tfarina | 3b0452d | 2014-12-31 15:20:09 | [diff] [blame] | 18 | #include "ui/gfx/geometry/rect_f.h" |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 19 | |
[email protected] | 768c547 | 2011-12-26 19:06:17 | [diff] [blame] | 20 | using content::WebContents; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 21 | |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 22 | namespace find_in_page { |
| 23 | |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 24 | // static |
[email protected] | c90c6ca | 2011-02-16 20:11:38 | [diff] [blame] | 25 | int FindTabHelper::find_request_id_counter_ = -1; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 26 | |
[email protected] | d908348 | 2012-01-06 00:38:46 | [diff] [blame] | 27 | FindTabHelper::FindTabHelper(WebContents* web_contents) |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 28 | : content::WebContentsUserData<FindTabHelper>(*web_contents), |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 29 | current_find_request_id_(find_request_id_counter_++), |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 30 | current_find_session_id_(current_find_request_id_) {} |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 31 | |
Evan Stade | 0e39ab76 | 2019-11-15 01:42:13 | [diff] [blame] | 32 | FindTabHelper::~FindTabHelper() { |
| 33 | for (auto& observer : observers_) |
| 34 | observer.OnFindTabHelperDestroyed(this); |
| 35 | } |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 36 | |
| 37 | void FindTabHelper::AddObserver(FindResultObserver* observer) { |
| 38 | observers_.AddObserver(observer); |
| 39 | } |
| 40 | |
| 41 | void FindTabHelper::RemoveObserver(FindResultObserver* observer) { |
| 42 | observers_.RemoveObserver(observer); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 43 | } |
| 44 | |
Jan Wilken Dörrie | fa241ba | 2021-03-11 17:57:01 | [diff] [blame] | 45 | void FindTabHelper::StartFinding(std::u16string search_string, |
[email protected] | c90c6ca | 2011-02-16 20:11:38 | [diff] [blame] | 46 | bool forward_direction, |
Rakina Zata Amni | d4ce97f | 2018-08-17 12:51:42 | [diff] [blame] | 47 | bool case_sensitive, |
Russell Davis | 715fe03 | 2020-09-16 01:51:09 | [diff] [blame] | 48 | bool find_match, |
Rakina Zata Amni | d4ce97f | 2018-08-17 12:51:42 | [diff] [blame] | 49 | bool run_synchronously_for_testing) { |
[email protected] | 102402c | 2014-07-09 19:53:38 | [diff] [blame] | 50 | // Remove the carriage return character, which generally isn't in web content. |
Jan Wilken Dörrie | cf67236 | 2021-03-15 14:16:01 | [diff] [blame] | 51 | const char16_t kInvalidChars[] = u"\r"; |
[email protected] | 102402c | 2014-07-09 19:53:38 | [diff] [blame] | 52 | base::RemoveChars(search_string, kInvalidChars, &search_string); |
Sakib Shabir Tantray | f889010a | 2025-01-31 10:01:59 | [diff] [blame] | 53 | is_find_session_active_ = true; |
[email protected] | 102402c | 2014-07-09 19:53:38 | [diff] [blame] | 54 | |
Russell Davis | 136df17 | 2020-10-15 18:00:42 | [diff] [blame] | 55 | // Keep track of what the last search was across the tabs. |
| 56 | if (delegate_) |
| 57 | delegate_->SetLastSearchText(search_string); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 58 | |
Russell Davis | 136df17 | 2020-10-15 18:00:42 | [diff] [blame] | 59 | if (search_string.empty()) { |
| 60 | StopFinding(find_in_page::SelectionAction::kClear); |
| 61 | for (auto& observer : observers_) |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 62 | observer.OnFindEmptyText(&GetWebContents()); |
Russell Davis | 136df17 | 2020-10-15 18:00:42 | [diff] [blame] | 63 | return; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 64 | } |
| 65 | |
Russell Davis | 136df17 | 2020-10-15 18:00:42 | [diff] [blame] | 66 | bool new_session = find_text_ != search_string || |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 67 | (last_search_case_sensitive_ != case_sensitive) || |
| 68 | find_op_aborted_; |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 69 | |
Russell Davis | dd2b678 | 2020-09-21 23:55:56 | [diff] [blame] | 70 | // Continuing here would just find the same results, potentially causing |
| 71 | // some flicker in the highlighting. |
| 72 | if (!new_session && !find_match) |
| 73 | return; |
| 74 | |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 75 | current_find_request_id_ = find_request_id_counter_++; |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 76 | if (new_session) |
paulmeyer | 1cfca29 | 2016-04-25 23:25:57 | [diff] [blame] | 77 | current_find_session_id_ = current_find_request_id_; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 78 | |
Russell Davis | 136df17 | 2020-10-15 18:00:42 | [diff] [blame] | 79 | previous_find_text_ = find_text_; |
| 80 | find_text_ = search_string; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 81 | last_search_case_sensitive_ = case_sensitive; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 82 | find_op_aborted_ = false; |
Russell Davis | 0fa45e9 | 2020-09-30 23:09:56 | [diff] [blame] | 83 | should_find_match_ = find_match; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 84 | |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 85 | auto options = blink::mojom::FindOptions::New(); |
| 86 | options->forward = forward_direction; |
| 87 | options->match_case = case_sensitive; |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 88 | options->new_session = new_session; |
Russell Davis | 715fe03 | 2020-09-16 01:51:09 | [diff] [blame] | 89 | options->find_match = find_match; |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 90 | options->run_synchronously_for_testing = run_synchronously_for_testing; |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 91 | GetWebContents().Find(current_find_request_id_, find_text_, |
Avi Drissman | 5710b06 | 2024-07-02 21:23:29 | [diff] [blame] | 92 | std::move(options), /*skip_delay=*/false); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 93 | } |
| 94 | |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 95 | void FindTabHelper::StopFinding(SelectionAction selection_action) { |
| 96 | if (selection_action == SelectionAction::kClear) { |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 97 | // kClearSelection means the find string has been cleared by the user, but |
| 98 | // the UI has not been dismissed. In that case we want to clear the |
| 99 | // previously remembered search (http://crbug.com/42639). |
Jan Wilken Dörrie | fa241ba | 2021-03-11 17:57:01 | [diff] [blame] | 100 | previous_find_text_ = std::u16string(); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 101 | } else { |
| 102 | find_ui_active_ = false; |
| 103 | if (!find_text_.empty()) |
| 104 | previous_find_text_ = find_text_; |
| 105 | } |
| 106 | find_text_.clear(); |
Eric Lawrence | 92b29bf | 2019-02-15 16:40:24 | [diff] [blame] | 107 | last_completed_find_text_.clear(); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 108 | find_op_aborted_ = true; |
| 109 | last_search_result_ = FindNotificationDetails(); |
Russell Davis | 0fa45e9 | 2020-09-30 23:09:56 | [diff] [blame] | 110 | should_find_match_ = false; |
Sakib Shabir Tantray | f889010a | 2025-01-31 10:01:59 | [diff] [blame] | 111 | is_find_session_active_ = false; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 112 | |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 113 | content::StopFindAction action; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 114 | switch (selection_action) { |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 115 | case SelectionAction::kClear: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 116 | action = content::STOP_FIND_ACTION_CLEAR_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 117 | break; |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 118 | case SelectionAction::kKeep: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 119 | action = content::STOP_FIND_ACTION_KEEP_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 120 | break; |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 121 | case SelectionAction::kActivate: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 122 | action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 123 | break; |
| 124 | default: |
Peter Boström | 77d2135 | 2024-11-13 22:26:11 | [diff] [blame] | 125 | NOTREACHED(); |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 126 | } |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 127 | GetWebContents().StopFinding(action); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 128 | } |
| 129 | |
dmazzoni | 1a69e2b3 | 2014-11-06 20:34:28 | [diff] [blame] | 130 | void FindTabHelper::ActivateFindInPageResultForAccessibility() { |
Dave Tapuska | 88d7b2e7 | 2022-06-07 21:00:51 | [diff] [blame] | 131 | GetWebContents() |
| 132 | .GetPrimaryMainFrame() |
| 133 | ->ActivateFindInPageResultForAccessibility(current_find_request_id_); |
dmazzoni | 1a69e2b3 | 2014-11-06 20:34:28 | [diff] [blame] | 134 | } |
| 135 | |
Jan Wilken Dörrie | fa241ba | 2021-03-11 17:57:01 | [diff] [blame] | 136 | std::u16string FindTabHelper::GetInitialSearchText() { |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 137 | // Try the last thing we searched for in this tab. |
| 138 | if (!previous_find_text_.empty()) |
| 139 | return previous_find_text_; |
| 140 | |
| 141 | // Then defer to the delegate. |
Jan Wilken Dörrie | fa241ba | 2021-03-11 17:57:01 | [diff] [blame] | 142 | return delegate_ ? delegate_->GetSearchPrepopulateText() : std::u16string(); |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 143 | } |
| 144 | |
Xiaohan Wang | bca91f9 | 2022-01-15 19:56:21 | [diff] [blame] | 145 | #if BUILDFLAG(IS_ANDROID) |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 146 | void FindTabHelper::ActivateNearestFindResult(float x, float y) { |
| 147 | if (!find_op_aborted_ && !find_text_.empty()) { |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 148 | GetWebContents().ActivateNearestFindResult(x, y); |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 149 | } |
| 150 | } |
| 151 | |
| 152 | void FindTabHelper::RequestFindMatchRects(int current_version) { |
| 153 | if (!find_op_aborted_ && !find_text_.empty()) |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 154 | GetWebContents().RequestFindMatchRects(current_version); |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 155 | } |
| 156 | #endif |
| 157 | |
[email protected] | b888919c | 2011-09-02 00:32:16 | [diff] [blame] | 158 | void FindTabHelper::HandleFindReply(int request_id, |
| 159 | int number_of_matches, |
| 160 | const gfx::Rect& selection_rect, |
| 161 | int active_match_ordinal, |
| 162 | bool final_update) { |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 163 | // Ignore responses for requests that have been aborted. |
paulmeyer | 1cfca29 | 2016-04-25 23:25:57 | [diff] [blame] | 164 | // Ignore responses for requests from previous sessions. That way we won't act |
| 165 | // on stale results when the user has already typed in another query. |
| 166 | if (!find_op_aborted_ && request_id >= current_find_session_id_) { |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 167 | if (number_of_matches == -1) |
| 168 | number_of_matches = last_search_result_.number_of_matches(); |
| 169 | if (active_match_ordinal == -1) |
| 170 | active_match_ordinal = last_search_result_.active_match_ordinal(); |
| 171 | |
| 172 | gfx::Rect selection = selection_rect; |
[email protected] | 27e7a055 | 2012-03-16 00:01:17 | [diff] [blame] | 173 | if (final_update && active_match_ordinal == 0) |
| 174 | selection = gfx::Rect(); |
| 175 | else if (selection_rect.IsEmpty()) |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 176 | selection = last_search_result_.selection_rect(); |
| 177 | |
| 178 | // Notify the UI, automation and any other observers that a find result was |
| 179 | // found. |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 180 | last_search_result_ = |
| 181 | FindNotificationDetails(request_id, number_of_matches, selection, |
| 182 | active_match_ordinal, final_update); |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 183 | for (auto& observer : observers_) |
Dave Tapuska | eeb6879 | 2021-11-16 21:38:40 | [diff] [blame] | 184 | observer.OnFindResultAvailable(&GetWebContents()); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 185 | } |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 186 | } |
François Doray | 4f51d5d | 2018-12-03 22:26:24 | [diff] [blame] | 187 | |
Daniel Cheng | 383df85 | 2021-10-02 03:28:01 | [diff] [blame] | 188 | WEB_CONTENTS_USER_DATA_KEY_IMPL(FindTabHelper); |
Evan Stade | 61ccab7 | 2020-01-17 20:17:51 | [diff] [blame] | 189 | |
| 190 | } // namespace find_in_page |