[email protected] | e9856c2 | 2012-01-27 01:51:59 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[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 | |
[email protected] | c90c6ca | 2011-02-16 20:11:38 | [diff] [blame] | 5 | #include "chrome/browser/ui/find_bar/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 | #include <vector> |
| 9 | |
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" |
[email protected] | 1a7ff74 | 2013-07-12 00:26:23 | [diff] [blame] | 12 | #include "chrome/browser/chrome_notification_types.h" |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 13 | #include "chrome/browser/profiles/profile.h" |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 14 | #include "chrome/browser/ui/find_bar/find_bar_state.h" |
[email protected] | e9856c2 | 2012-01-27 01:51:59 | [diff] [blame] | 15 | #include "chrome/browser/ui/find_bar/find_bar_state_factory.h" |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 16 | #include "chrome/browser/ui/find_bar/find_result_observer.h" |
| 17 | #include "chrome/browser/ui/find_bar/find_types.h" |
[email protected] | ad50def5 | 2011-10-19 23:17:07 | [diff] [blame] | 18 | #include "content/public/browser/notification_service.h" |
dmazzoni | 1a69e2b3 | 2014-11-06 20:34:28 | [diff] [blame] | 19 | #include "content/public/browser/render_frame_host.h" |
[email protected] | 9c1662b | 2012-03-06 15:44:33 | [diff] [blame] | 20 | #include "content/public/browser/render_view_host.h" |
[email protected] | d908348 | 2012-01-06 00:38:46 | [diff] [blame] | 21 | #include "content/public/browser/web_contents.h" |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 22 | #include "content/public/common/stop_find_action.h" |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 23 | #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" |
tfarina | 3b0452d | 2014-12-31 15:20:09 | [diff] [blame] | 24 | #include "ui/gfx/geometry/rect_f.h" |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 25 | |
[email protected] | 768c547 | 2011-12-26 19:06:17 | [diff] [blame] | 26 | using content::WebContents; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 27 | |
| 28 | // static |
[email protected] | c90c6ca | 2011-02-16 20:11:38 | [diff] [blame] | 29 | int FindTabHelper::find_request_id_counter_ = -1; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 30 | |
[email protected] | d908348 | 2012-01-06 00:38:46 | [diff] [blame] | 31 | FindTabHelper::FindTabHelper(WebContents* web_contents) |
| 32 | : content::WebContentsObserver(web_contents), |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 33 | find_ui_active_(false), |
| 34 | find_op_aborted_(false), |
| 35 | current_find_request_id_(find_request_id_counter_++), |
paulmeyer | 1cfca29 | 2016-04-25 23:25:57 | [diff] [blame] | 36 | current_find_session_id_(current_find_request_id_), |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 37 | last_search_case_sensitive_(false), |
| 38 | last_search_result_() { |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 39 | } |
| 40 | |
Evan Stade | 0e39ab76 | 2019-11-15 01:42:13 | [diff] [blame^] | 41 | FindTabHelper::~FindTabHelper() { |
| 42 | for (auto& observer : observers_) |
| 43 | observer.OnFindTabHelperDestroyed(this); |
| 44 | } |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 45 | |
| 46 | void FindTabHelper::AddObserver(FindResultObserver* observer) { |
| 47 | observers_.AddObserver(observer); |
| 48 | } |
| 49 | |
| 50 | void FindTabHelper::RemoveObserver(FindResultObserver* observer) { |
| 51 | observers_.RemoveObserver(observer); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 52 | } |
| 53 | |
[email protected] | b959d7d4 | 2013-12-13 17:26:37 | [diff] [blame] | 54 | void FindTabHelper::StartFinding(base::string16 search_string, |
[email protected] | c90c6ca | 2011-02-16 20:11:38 | [diff] [blame] | 55 | bool forward_direction, |
Rakina Zata Amni | d4ce97f | 2018-08-17 12:51:42 | [diff] [blame] | 56 | bool case_sensitive, |
| 57 | bool run_synchronously_for_testing) { |
[email protected] | 102402c | 2014-07-09 19:53:38 | [diff] [blame] | 58 | // 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] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 62 | // 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] | 4c1865a | 2011-08-01 14:30:39 | [diff] [blame] | 65 | Profile* profile = |
[email protected] | 768c547 | 2011-12-26 19:06:17 | [diff] [blame] | 66 | Profile::FromBrowserContext(web_contents()->GetBrowserContext()); |
[email protected] | a04db82 | 2013-12-11 19:14:40 | [diff] [blame] | 67 | base::string16 last_search_prepopulate_text = |
[email protected] | e9856c2 | 2012-01-27 01:51:59 | [diff] [blame] | 68 | FindBarStateFactory::GetLastPrepopulateText(profile); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 69 | |
| 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_; |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 92 | |
| 93 | current_find_request_id_ = find_request_id_counter_++; |
paulmeyer | 1cfca29 | 2016-04-25 23:25:57 | [diff] [blame] | 94 | if (!find_next) |
| 95 | current_find_session_id_ = current_find_request_id_; |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 96 | |
| 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] | 4c1865a | 2011-08-01 14:30:39 | [diff] [blame] | 104 | Profile* profile = |
[email protected] | 768c547 | 2011-12-26 19:06:17 | [diff] [blame] | 105 | Profile::FromBrowserContext(web_contents()->GetBrowserContext()); |
[email protected] | e9856c2 | 2012-01-27 01:51:59 | [diff] [blame] | 106 | FindBarState* find_bar_state = FindBarStateFactory::GetForProfile(profile); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 107 | find_bar_state->set_last_prepopulate_text(find_text_); |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 108 | |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 109 | 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] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 116 | } |
| 117 | |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 118 | void FindTabHelper::StopFinding(FindOnPageSelectionAction selection_action) { |
| 119 | if (selection_action == FindOnPageSelectionAction::kClear) { |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 120 | // 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] | a04db82 | 2013-12-11 19:14:40 | [diff] [blame] | 123 | previous_find_text_ = base::string16(); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 124 | } else { |
| 125 | find_ui_active_ = false; |
| 126 | if (!find_text_.empty()) |
| 127 | previous_find_text_ = find_text_; |
| 128 | } |
| 129 | find_text_.clear(); |
Eric Lawrence | 92b29bf | 2019-02-15 16:40:24 | [diff] [blame] | 130 | last_completed_find_text_.clear(); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 131 | find_op_aborted_ = true; |
| 132 | last_search_result_ = FindNotificationDetails(); |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 133 | |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 134 | content::StopFindAction action; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 135 | switch (selection_action) { |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 136 | case FindOnPageSelectionAction::kClear: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 137 | action = content::STOP_FIND_ACTION_CLEAR_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 138 | break; |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 139 | case FindOnPageSelectionAction::kKeep: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 140 | action = content::STOP_FIND_ACTION_KEEP_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 141 | break; |
Evan Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 142 | case FindOnPageSelectionAction::kActivate: |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 143 | action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 144 | break; |
| 145 | default: |
| 146 | NOTREACHED(); |
[email protected] | 815dd987 | 2011-11-23 18:40:30 | [diff] [blame] | 147 | action = content::STOP_FIND_ACTION_KEEP_SELECTION; |
[email protected] | 21681395 | 2011-05-19 22:21:26 | [diff] [blame] | 148 | } |
[email protected] | 36ec24f | 2014-01-09 00:32:08 | [diff] [blame] | 149 | web_contents()->StopFinding(action); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 150 | } |
| 151 | |
dmazzoni | 1a69e2b3 | 2014-11-06 20:34:28 | [diff] [blame] | 152 | void FindTabHelper::ActivateFindInPageResultForAccessibility() { |
| 153 | web_contents()->GetMainFrame()->ActivateFindInPageResultForAccessibility( |
| 154 | current_find_request_id_); |
| 155 | } |
| 156 | |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 157 | #if defined(OS_ANDROID) |
| 158 | void FindTabHelper::ActivateNearestFindResult(float x, float y) { |
| 159 | if (!find_op_aborted_ && !find_text_.empty()) { |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 160 | web_contents()->ActivateNearestFindResult(x, y); |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | |
| 164 | void FindTabHelper::RequestFindMatchRects(int current_version) { |
| 165 | if (!find_op_aborted_ && !find_text_.empty()) |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 166 | web_contents()->RequestFindMatchRects(current_version); |
[email protected] | 59363fc9 | 2012-09-05 03:46:31 | [diff] [blame] | 167 | } |
| 168 | #endif |
| 169 | |
[email protected] | b888919c | 2011-09-02 00:32:16 | [diff] [blame] | 170 | void 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] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 175 | // Ignore responses for requests that have been aborted. |
paulmeyer | 1cfca29 | 2016-04-25 23:25:57 | [diff] [blame] | 176 | // 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] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 179 | 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] | 27e7a055 | 2012-03-16 00:01:17 | [diff] [blame] | 185 | if (final_update && active_match_ordinal == 0) |
| 186 | selection = gfx::Rect(); |
| 187 | else if (selection_rect.IsEmpty()) |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 188 | 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 Stade | 70a3cef | 2019-07-11 20:45:36 | [diff] [blame] | 195 | for (auto& observer : observers_) |
| 196 | observer.OnFindResultAvailable(web_contents()); |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 197 | } |
[email protected] | 9c31886 | 2011-02-01 22:27:24 | [diff] [blame] | 198 | } |
François Doray | 4f51d5d | 2018-12-03 22:26:24 | [diff] [blame] | 199 | |
| 200 | WEB_CONTENTS_USER_DATA_KEY_IMPL(FindTabHelper) |