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 "extensions/browser/api/guest_view/web_view/web_view_internal_api.h"
10 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
12 namespace extensions
{
14 WebViewFindHelper::WebViewFindHelper(WebViewGuest
* webview_guest
)
15 : webview_guest_(webview_guest
), current_find_request_id_(0) {
18 WebViewFindHelper::~WebViewFindHelper() {
21 void WebViewFindHelper::CancelAllFindSessions() {
22 current_find_session_
= linked_ptr
<WebViewFindHelper::FindInfo
>();
23 while (!find_info_map_
.empty()) {
24 find_info_map_
.begin()->second
->SendResponse(true /* canceled */);
25 find_info_map_
.erase(find_info_map_
.begin());
27 if (find_update_event_
.get())
28 DispatchFindUpdateEvent(true /* canceled */, true /* final_update */);
29 find_update_event_
.reset();
32 void WebViewFindHelper::DispatchFindUpdateEvent(bool canceled
,
34 DCHECK(find_update_event_
.get());
35 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
36 find_update_event_
->PrepareResults(args
.get());
37 args
->SetBoolean(webview::kFindCanceled
, canceled
);
38 args
->SetBoolean(webview::kFindFinalUpdate
, final_update
);
39 DCHECK(webview_guest_
);
40 webview_guest_
->DispatchEventToView(
41 new GuestViewBase::Event(webview::kEventFindReply
, args
.Pass()));
44 void WebViewFindHelper::EndFindSession(int session_request_id
, bool canceled
) {
45 FindInfoMap::iterator session_iterator
=
46 find_info_map_
.find(session_request_id
);
47 DCHECK(session_iterator
!= find_info_map_
.end());
48 FindInfo
* find_info
= session_iterator
->second
.get();
50 // Call the callback function of the first request of the find session.
51 find_info
->SendResponse(canceled
);
53 // For every subsequent find request of the find session.
54 for (std::vector
<base::WeakPtr
<WebViewFindHelper::FindInfo
> >::iterator i
=
55 find_info
->find_next_requests_
.begin();
56 i
!= find_info
->find_next_requests_
.end();
60 // Do not call callbacks for subsequent find requests that have not been
61 // replied to yet. These requests will get their own final updates in the
62 // same order as they appear in |find_next_requests_|, i.e. the order that
63 // the requests were made in. Once one request is found that has not been
64 // replied to, none that follow will be replied to either, and do not need
69 // Update the request's number of matches (if not canceled).
71 (*i
)->find_results_
.number_of_matches_
=
72 find_info
->find_results_
.number_of_matches_
;
75 // Call the request's callback function with the find results, and then
76 // delete its map entry to free the WebViewInternalFindFunction object.
77 (*i
)->SendResponse(canceled
);
78 find_info_map_
.erase((*i
)->request_id_
);
81 // Erase the first find request's map entry to free the
82 // WebViewInternalFindFunction
84 find_info_map_
.erase(session_request_id
);
87 void WebViewFindHelper::Find(
88 content::WebContents
* guest_web_contents
,
89 const base::string16
& search_text
,
90 const blink::WebFindOptions
& options
,
91 scoped_refptr
<WebViewInternalFindFunction
> find_function
) {
92 // Need a new request_id for each new find request.
93 ++current_find_request_id_
;
95 // Stores the find request information by request_id so that its callback
96 // function can be called when the find results are available.
97 std::pair
<FindInfoMap::iterator
, bool> insert_result
=
98 find_info_map_
.insert(std::make_pair(
99 current_find_request_id_
,
101 WebViewFindHelper::FindInfo
>(new WebViewFindHelper::FindInfo(
102 current_find_request_id_
, search_text
, options
, find_function
))));
103 // No duplicate insertions.
104 DCHECK(insert_result
.second
);
106 // Find options including the implicit |findNext| field.
107 blink::WebFindOptions
* full_options
= insert_result
.first
->second
->options();
109 // Set |findNext| implicitly.
110 if (current_find_session_
.get()) {
111 const base::string16
& current_search_text
=
112 current_find_session_
->search_text();
113 bool current_match_case
= current_find_session_
->options()->matchCase
;
114 full_options
->findNext
= !current_search_text
.empty() &&
115 current_search_text
== search_text
&&
116 current_match_case
== options
.matchCase
;
118 full_options
->findNext
= false;
121 // Link find requests that are a part of the same find session.
122 if (full_options
->findNext
&& current_find_session_
.get()) {
123 DCHECK(current_find_request_id_
!= current_find_session_
->request_id());
124 current_find_session_
->AddFindNextRequest(
125 insert_result
.first
->second
->AsWeakPtr());
128 // Update the current find session, if necessary.
129 if (!full_options
->findNext
)
130 current_find_session_
= insert_result
.first
->second
;
132 guest_web_contents
->Find(current_find_request_id_
,
133 search_text
, *full_options
);
136 void WebViewFindHelper::FindReply(int request_id
,
137 int number_of_matches
,
138 const gfx::Rect
& selection_rect
,
139 int active_match_ordinal
,
141 FindInfoMap::iterator find_iterator
= find_info_map_
.find(request_id
);
143 // Ignore slow replies to canceled find requests.
144 if (find_iterator
== find_info_map_
.end())
147 // This find request must be a part of an existing find session.
148 DCHECK(current_find_session_
.get());
150 WebViewFindHelper::FindInfo
* find_info
= find_iterator
->second
.get();
152 // Handle canceled find requests.
153 if (!find_info
->options()->findNext
&&
154 find_info_map_
.begin()->first
< request_id
) {
155 DCHECK_NE(current_find_session_
->request_id(),
156 find_info_map_
.begin()->first
);
157 DispatchFindUpdateEvent(true /* canceled */, true /* final_update */);
158 EndFindSession(find_info_map_
.begin()->first
, true /* canceled */);
161 // Clears the results for |findupdate| for a new find session.
162 if (!find_info
->replied() && !find_info
->options()->findNext
)
163 find_update_event_
.reset(new FindUpdateEvent(find_info
->search_text()));
165 // Aggregate the find results.
166 find_info
->AggregateResults(number_of_matches
, selection_rect
,
167 active_match_ordinal
, final_update
);
168 find_update_event_
->AggregateResults(number_of_matches
, selection_rect
,
169 active_match_ordinal
, final_update
);
171 // Propagate incremental results to the |findupdate| event.
172 DispatchFindUpdateEvent(false /* canceled */, final_update
);
174 // Call the callback functions of completed find requests.
176 EndFindSession(request_id
, false /* canceled */);
179 WebViewFindHelper::FindResults::FindResults()
180 : number_of_matches_(0), active_match_ordinal_(0) {
183 WebViewFindHelper::FindResults::~FindResults() {
186 void WebViewFindHelper::FindResults::AggregateResults(
187 int number_of_matches
,
188 const gfx::Rect
& selection_rect
,
189 int active_match_ordinal
,
191 if (number_of_matches
!= -1)
192 number_of_matches_
= number_of_matches
;
194 if (active_match_ordinal
!= -1)
195 active_match_ordinal_
= active_match_ordinal
;
197 if (final_update
&& active_match_ordinal_
== 0) {
198 // No match found, so the selection rectangle is empty.
199 selection_rect_
= gfx::Rect();
200 } else if (!selection_rect
.IsEmpty()) {
201 selection_rect_
= selection_rect
;
205 void WebViewFindHelper::FindResults::PrepareResults(
206 base::DictionaryValue
* results
) {
207 results
->SetInteger(webview::kFindNumberOfMatches
, number_of_matches_
);
208 results
->SetInteger(webview::kFindActiveMatchOrdinal
, active_match_ordinal_
);
209 base::DictionaryValue rect
;
210 rect
.SetInteger(webview::kFindRectLeft
, selection_rect_
.x());
211 rect
.SetInteger(webview::kFindRectTop
, selection_rect_
.y());
212 rect
.SetInteger(webview::kFindRectWidth
, selection_rect_
.width());
213 rect
.SetInteger(webview::kFindRectHeight
, selection_rect_
.height());
214 results
->Set(webview::kFindSelectionRect
, rect
.DeepCopy());
217 WebViewFindHelper::FindUpdateEvent::FindUpdateEvent(
218 const base::string16
& search_text
)
219 : search_text_(search_text
) {
222 WebViewFindHelper::FindUpdateEvent::~FindUpdateEvent() {
225 void WebViewFindHelper::FindUpdateEvent::AggregateResults(
226 int number_of_matches
,
227 const gfx::Rect
& selection_rect
,
228 int active_match_ordinal
,
230 find_results_
.AggregateResults(number_of_matches
, selection_rect
,
231 active_match_ordinal
, final_update
);
234 void WebViewFindHelper::FindUpdateEvent::PrepareResults(
235 base::DictionaryValue
* results
) {
236 results
->SetString(webview::kFindSearchText
, search_text_
);
237 find_results_
.PrepareResults(results
);
240 WebViewFindHelper::FindInfo::FindInfo(
242 const base::string16
& search_text
,
243 const blink::WebFindOptions
& options
,
244 scoped_refptr
<WebViewInternalFindFunction
> find_function
)
245 : request_id_(request_id
),
246 search_text_(search_text
),
248 find_function_(find_function
),
250 weak_ptr_factory_(this) {
253 WebViewFindHelper::FindInfo::~FindInfo() {
256 void WebViewFindHelper::FindInfo::AggregateResults(
257 int number_of_matches
,
258 const gfx::Rect
& selection_rect
,
259 int active_match_ordinal
,
262 find_results_
.AggregateResults(number_of_matches
, selection_rect
,
263 active_match_ordinal
, final_update
);
266 base::WeakPtr
<WebViewFindHelper::FindInfo
>
267 WebViewFindHelper::FindInfo::AsWeakPtr() {
268 return weak_ptr_factory_
.GetWeakPtr();
271 void WebViewFindHelper::FindInfo::SendResponse(bool canceled
) {
272 // Prepare the find results to pass to the callback function.
273 base::DictionaryValue results
;
274 find_results_
.PrepareResults(&results
);
275 results
.SetBoolean(webview::kFindCanceled
, canceled
);
277 // Call the callback.
278 find_function_
->SetResult(results
.DeepCopy());
279 find_function_
->SendResponse(true);
282 } // namespace extensions