1 // Copyright 2014 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 "extensions/browser/guest_view/web_view/web_view_find_helper.h"
9 #include "components/guest_view/browser/guest_view_event.h"
10 #include "extensions/browser/api/guest_view/web_view/web_view_internal_api.h"
11 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
13 using guest_view::GuestViewEvent
;
15 namespace extensions
{
17 WebViewFindHelper::WebViewFindHelper(WebViewGuest
* webview_guest
)
18 : webview_guest_(webview_guest
), current_find_request_id_(0) {
21 WebViewFindHelper::~WebViewFindHelper() {
24 void WebViewFindHelper::CancelAllFindSessions() {
25 current_find_session_
= linked_ptr
<WebViewFindHelper::FindInfo
>();
26 while (!find_info_map_
.empty()) {
27 find_info_map_
.begin()->second
->SendResponse(true /* canceled */);
28 find_info_map_
.erase(find_info_map_
.begin());
30 if (find_update_event_
.get())
31 DispatchFindUpdateEvent(true /* canceled */, true /* final_update */);
32 find_update_event_
.reset();
35 void WebViewFindHelper::DispatchFindUpdateEvent(bool canceled
,
37 DCHECK(find_update_event_
.get());
38 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
39 find_update_event_
->PrepareResults(args
.get());
40 args
->SetBoolean(webview::kFindCanceled
, canceled
);
41 args
->SetBoolean(webview::kFindFinalUpdate
, final_update
);
42 DCHECK(webview_guest_
);
43 webview_guest_
->DispatchEventToView(
44 new GuestViewEvent(webview::kEventFindReply
, args
.Pass()));
47 void WebViewFindHelper::EndFindSession(int session_request_id
, bool canceled
) {
48 FindInfoMap::iterator session_iterator
=
49 find_info_map_
.find(session_request_id
);
50 DCHECK(session_iterator
!= find_info_map_
.end());
51 FindInfo
* find_info
= session_iterator
->second
.get();
53 // Call the callback function of the first request of the find session.
54 find_info
->SendResponse(canceled
);
56 // For every subsequent find request of the find session.
57 for (std::vector
<base::WeakPtr
<WebViewFindHelper::FindInfo
> >::iterator i
=
58 find_info
->find_next_requests_
.begin();
59 i
!= find_info
->find_next_requests_
.end();
63 // Do not call callbacks for subsequent find requests that have not been
64 // replied to yet. These requests will get their own final updates in the
65 // same order as they appear in |find_next_requests_|, i.e. the order that
66 // the requests were made in. Once one request is found that has not been
67 // replied to, none that follow will be replied to either, and do not need
72 // Update the request's number of matches (if not canceled).
74 (*i
)->find_results_
.number_of_matches_
=
75 find_info
->find_results_
.number_of_matches_
;
78 // Call the request's callback function with the find results, and then
79 // delete its map entry to free the WebViewInternalFindFunction object.
80 (*i
)->SendResponse(canceled
);
81 find_info_map_
.erase((*i
)->request_id_
);
84 // Erase the first find request's map entry to free the
85 // WebViewInternalFindFunction
87 find_info_map_
.erase(session_request_id
);
90 void WebViewFindHelper::Find(
91 content::WebContents
* guest_web_contents
,
92 const base::string16
& search_text
,
93 const blink::WebFindOptions
& options
,
94 scoped_refptr
<WebViewInternalFindFunction
> find_function
) {
95 // Need a new request_id for each new find request.
96 ++current_find_request_id_
;
98 // Stores the find request information by request_id so that its callback
99 // function can be called when the find results are available.
100 std::pair
<FindInfoMap::iterator
, bool> insert_result
=
101 find_info_map_
.insert(std::make_pair(
102 current_find_request_id_
,
104 WebViewFindHelper::FindInfo
>(new WebViewFindHelper::FindInfo(
105 current_find_request_id_
, search_text
, options
, find_function
))));
106 // No duplicate insertions.
107 DCHECK(insert_result
.second
);
109 // Find options including the implicit |findNext| field.
110 blink::WebFindOptions
* full_options
= insert_result
.first
->second
->options();
112 // Set |findNext| implicitly.
113 if (current_find_session_
.get()) {
114 const base::string16
& current_search_text
=
115 current_find_session_
->search_text();
116 bool current_match_case
= current_find_session_
->options()->matchCase
;
117 full_options
->findNext
= !current_search_text
.empty() &&
118 current_search_text
== search_text
&&
119 current_match_case
== options
.matchCase
;
121 full_options
->findNext
= false;
124 // Link find requests that are a part of the same find session.
125 if (full_options
->findNext
&& current_find_session_
.get()) {
126 DCHECK(current_find_request_id_
!= current_find_session_
->request_id());
127 current_find_session_
->AddFindNextRequest(
128 insert_result
.first
->second
->AsWeakPtr());
131 // Update the current find session, if necessary.
132 if (!full_options
->findNext
)
133 current_find_session_
= insert_result
.first
->second
;
135 guest_web_contents
->Find(current_find_request_id_
,
136 search_text
, *full_options
);
139 void WebViewFindHelper::FindReply(int request_id
,
140 int number_of_matches
,
141 const gfx::Rect
& selection_rect
,
142 int active_match_ordinal
,
144 FindInfoMap::iterator find_iterator
= find_info_map_
.find(request_id
);
146 // Ignore slow replies to canceled find requests.
147 if (find_iterator
== find_info_map_
.end())
150 // This find request must be a part of an existing find session.
151 DCHECK(current_find_session_
.get());
153 WebViewFindHelper::FindInfo
* find_info
= find_iterator
->second
.get();
155 // Handle canceled find requests.
156 if (!find_info
->options()->findNext
&&
157 find_info_map_
.begin()->first
< request_id
) {
158 DCHECK_NE(current_find_session_
->request_id(),
159 find_info_map_
.begin()->first
);
160 DispatchFindUpdateEvent(true /* canceled */, true /* final_update */);
161 EndFindSession(find_info_map_
.begin()->first
, true /* canceled */);
164 // Clears the results for |findupdate| for a new find session.
165 if (!find_info
->replied() && !find_info
->options()->findNext
)
166 find_update_event_
.reset(new FindUpdateEvent(find_info
->search_text()));
168 // Aggregate the find results.
169 find_info
->AggregateResults(number_of_matches
, selection_rect
,
170 active_match_ordinal
, final_update
);
171 find_update_event_
->AggregateResults(number_of_matches
, selection_rect
,
172 active_match_ordinal
, final_update
);
174 // Propagate incremental results to the |findupdate| event.
175 DispatchFindUpdateEvent(false /* canceled */, final_update
);
177 // Call the callback functions of completed find requests.
179 EndFindSession(request_id
, false /* canceled */);
182 WebViewFindHelper::FindResults::FindResults()
183 : number_of_matches_(0), active_match_ordinal_(0) {
186 WebViewFindHelper::FindResults::~FindResults() {
189 void WebViewFindHelper::FindResults::AggregateResults(
190 int number_of_matches
,
191 const gfx::Rect
& selection_rect
,
192 int active_match_ordinal
,
194 if (number_of_matches
!= -1)
195 number_of_matches_
= number_of_matches
;
197 if (active_match_ordinal
!= -1)
198 active_match_ordinal_
= active_match_ordinal
;
200 if (final_update
&& active_match_ordinal_
== 0) {
201 // No match found, so the selection rectangle is empty.
202 selection_rect_
= gfx::Rect();
203 } else if (!selection_rect
.IsEmpty()) {
204 selection_rect_
= selection_rect
;
208 void WebViewFindHelper::FindResults::PrepareResults(
209 base::DictionaryValue
* results
) {
210 results
->SetInteger(webview::kFindNumberOfMatches
, number_of_matches_
);
211 results
->SetInteger(webview::kFindActiveMatchOrdinal
, active_match_ordinal_
);
212 base::DictionaryValue rect
;
213 rect
.SetInteger(webview::kFindRectLeft
, selection_rect_
.x());
214 rect
.SetInteger(webview::kFindRectTop
, selection_rect_
.y());
215 rect
.SetInteger(webview::kFindRectWidth
, selection_rect_
.width());
216 rect
.SetInteger(webview::kFindRectHeight
, selection_rect_
.height());
217 results
->Set(webview::kFindSelectionRect
, rect
.DeepCopy());
220 WebViewFindHelper::FindUpdateEvent::FindUpdateEvent(
221 const base::string16
& search_text
)
222 : search_text_(search_text
) {
225 WebViewFindHelper::FindUpdateEvent::~FindUpdateEvent() {
228 void WebViewFindHelper::FindUpdateEvent::AggregateResults(
229 int number_of_matches
,
230 const gfx::Rect
& selection_rect
,
231 int active_match_ordinal
,
233 find_results_
.AggregateResults(number_of_matches
, selection_rect
,
234 active_match_ordinal
, final_update
);
237 void WebViewFindHelper::FindUpdateEvent::PrepareResults(
238 base::DictionaryValue
* results
) {
239 results
->SetString(webview::kFindSearchText
, search_text_
);
240 find_results_
.PrepareResults(results
);
243 WebViewFindHelper::FindInfo::FindInfo(
245 const base::string16
& search_text
,
246 const blink::WebFindOptions
& options
,
247 scoped_refptr
<WebViewInternalFindFunction
> find_function
)
248 : request_id_(request_id
),
249 search_text_(search_text
),
251 find_function_(find_function
),
253 weak_ptr_factory_(this) {
256 WebViewFindHelper::FindInfo::~FindInfo() {
259 void WebViewFindHelper::FindInfo::AggregateResults(
260 int number_of_matches
,
261 const gfx::Rect
& selection_rect
,
262 int active_match_ordinal
,
265 find_results_
.AggregateResults(number_of_matches
, selection_rect
,
266 active_match_ordinal
, final_update
);
269 base::WeakPtr
<WebViewFindHelper::FindInfo
>
270 WebViewFindHelper::FindInfo::AsWeakPtr() {
271 return weak_ptr_factory_
.GetWeakPtr();
274 void WebViewFindHelper::FindInfo::SendResponse(bool canceled
) {
275 // Prepare the find results to pass to the callback function.
276 base::DictionaryValue results
;
277 find_results_
.PrepareResults(&results
);
278 results
.SetBoolean(webview::kFindCanceled
, canceled
);
280 // Call the callback.
281 find_function_
->SetResult(results
.DeepCopy());
282 find_function_
->SendResponse(true);
285 } // namespace extensions