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