Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / guestview / webview / webview_find_helper.cc
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.
4
5 #include "chrome/browser/guestview/webview/webview_find_helper.h"
6
7 #include <utility>
8
9 #include "chrome/browser/extensions/api/webview/webview_api.h"
10 #include "chrome/browser/guestview/webview/webview_constants.h"
11
12 WebviewFindHelper::WebviewFindHelper(WebViewGuest* webview_guest)
13     : webview_guest_(webview_guest),
14       current_find_request_id_(0) {
15 }
16
17 WebviewFindHelper::~WebviewFindHelper() {
18 }
19
20 void WebviewFindHelper::CancelAllFindSessions() {
21   current_find_session_ = linked_ptr<WebviewFindHelper::FindInfo>();
22   while (!find_info_map_.empty()) {
23     find_info_map_.begin()->second->SendResponse(true /* canceled */);
24     find_info_map_.erase(find_info_map_.begin());
25   }
26   if (find_update_event_.get())
27     DispatchFindUpdateEvent(true /* canceled */, true /* final_update */);
28   find_update_event_.reset();
29 }
30
31 void WebviewFindHelper::DispatchFindUpdateEvent(bool canceled,
32                                                 bool final_update) {
33   DCHECK(find_update_event_.get());
34   scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
35   find_update_event_->PrepareResults(args.get());
36   args->SetBoolean(webview::kFindCanceled, canceled);
37   args->SetBoolean(webview::kFindFinalUpdate, final_update);
38   DCHECK(webview_guest_);
39   webview_guest_->DispatchEvent(new GuestView::Event(webview::kEventFindReply,
40                                                      args.Pass()));
41 }
42
43 void WebviewFindHelper::EndFindSession(int session_request_id, bool canceled) {
44   FindInfoMap::iterator session_iterator =
45       find_info_map_.find(session_request_id);
46   DCHECK(session_iterator != find_info_map_.end());
47   FindInfo* find_info = session_iterator->second.get();
48
49   // Call the callback function of the first request of the find session.
50   find_info->SendResponse(canceled);
51
52   // For every subsequent find request of the find session.
53   for (std::vector<base::WeakPtr<WebviewFindHelper::FindInfo> >::iterator i =
54            find_info->find_next_requests_.begin();
55        i != find_info->find_next_requests_.end(); ++i) {
56     DCHECK(i->get());
57
58     // Do not call callbacks for subsequent find requests that have not been
59     // replied to yet. These requests will get their own final updates in the
60     // same order as they appear in |find_next_requests_|, i.e. the order that
61     // the requests were made in. Once one request is found that has not been
62     // replied to, none that follow will be replied to either, and do not need
63     // to be checked.
64     if (!(*i)->replied_)
65       break;
66
67     // Update the request's number of matches (if not canceled).
68     if (!canceled) {
69       (*i)->find_results_.number_of_matches_ =
70           find_info->find_results_.number_of_matches_;
71     }
72
73     // Call the request's callback function with the find results, and then
74     // delete its map entry to free the WebviewFindFunction object.
75     (*i)->SendResponse(canceled);
76     find_info_map_.erase((*i)->request_id_);
77   }
78
79   // Erase the first find request's map entry to free the WebviewFindFunction
80   // object.
81   find_info_map_.erase(session_request_id);
82 }
83
84 void WebviewFindHelper::Find(
85     content::WebContents* guest_web_contents,
86     const base::string16& search_text,
87     const blink::WebFindOptions& options,
88     scoped_refptr<extensions::WebviewFindFunction> find_function) {
89   // Need a new request_id for each new find request.
90   ++current_find_request_id_;
91
92   // Stores the find request information by request_id so that its callback
93   // function can be called when the find results are available.
94   std::pair<FindInfoMap::iterator, bool> insert_result =
95       find_info_map_.insert(
96           std::make_pair(current_find_request_id_,
97                          linked_ptr<WebviewFindHelper::FindInfo>(
98                              new WebviewFindHelper::FindInfo(
99                                  current_find_request_id_,
100                                  search_text,
101                                  options,
102                                  find_function))));
103   // No duplicate insertions.
104   DCHECK(insert_result.second);
105
106   // Find options including the implicit |findNext| field.
107   blink::WebFindOptions* full_options = insert_result.first->second->options();
108
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;
117   } else {
118     full_options->findNext = false;
119   }
120
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());
126   }
127
128   // Update the current find session, if necessary.
129   if (!full_options->findNext)
130     current_find_session_ = insert_result.first->second;
131
132   guest_web_contents->Find(current_find_request_id_,
133                            search_text, *full_options);
134 }
135
136 void WebviewFindHelper::FindReply(int request_id,
137                                   int number_of_matches,
138                                   const gfx::Rect& selection_rect,
139                                   int active_match_ordinal,
140                                   bool final_update) {
141   FindInfoMap::iterator find_iterator = find_info_map_.find(request_id);
142
143   // Ignore slow replies to canceled find requests.
144   if (find_iterator == find_info_map_.end())
145     return;
146
147   // This find request must be a part of an existing find session.
148   DCHECK(current_find_session_.get());
149
150   WebviewFindHelper::FindInfo* find_info = find_iterator->second.get();
151
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 */);
159   }
160
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()));
164
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);
170
171   // Propagate incremental results to the |findupdate| event.
172   DispatchFindUpdateEvent(false /* canceled */, final_update);
173
174   // Call the callback functions of completed find requests.
175   if (final_update)
176     EndFindSession(request_id, false /* canceled */);
177 }
178
179 WebviewFindHelper::FindResults::FindResults()
180     : number_of_matches_(0),
181       active_match_ordinal_(0) {}
182
183 WebviewFindHelper::FindResults::~FindResults() {
184 }
185
186 void WebviewFindHelper::FindResults::AggregateResults(
187     int number_of_matches,
188     const gfx::Rect& selection_rect,
189     int active_match_ordinal,
190     bool final_update) {
191   if (number_of_matches != -1)
192     number_of_matches_ = number_of_matches;
193
194   if (active_match_ordinal != -1)
195     active_match_ordinal_ = active_match_ordinal;
196
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;
202   }
203 }
204
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());
215 }
216
217 WebviewFindHelper::FindUpdateEvent::FindUpdateEvent(
218     const base::string16& search_text) : search_text_(search_text) {
219 }
220
221 WebviewFindHelper::FindUpdateEvent::~FindUpdateEvent() {
222 }
223
224 void WebviewFindHelper::FindUpdateEvent::AggregateResults(
225     int number_of_matches,
226     const gfx::Rect& selection_rect,
227     int active_match_ordinal,
228     bool final_update) {
229   find_results_.AggregateResults(number_of_matches, selection_rect,
230                                  active_match_ordinal, final_update);
231 }
232
233 void WebviewFindHelper::FindUpdateEvent::PrepareResults(
234     base::DictionaryValue* results) {
235   results->SetString(webview::kFindSearchText, search_text_);
236   find_results_.PrepareResults(results);
237 }
238
239 WebviewFindHelper::FindInfo::FindInfo(
240     int request_id,
241     const base::string16& search_text,
242     const blink::WebFindOptions& options,
243     scoped_refptr<extensions::WebviewFindFunction> find_function)
244     : request_id_(request_id),
245       search_text_(search_text),
246       options_(options),
247       find_function_(find_function),
248       replied_(false),
249       weak_ptr_factory_(this) {
250 }
251
252 WebviewFindHelper::FindInfo::~FindInfo() {
253 }
254
255 void WebviewFindHelper::FindInfo::AggregateResults(
256     int number_of_matches,
257     const gfx::Rect& selection_rect,
258     int active_match_ordinal,
259     bool final_update) {
260   replied_ = true;
261   find_results_.AggregateResults(number_of_matches, selection_rect,
262                                  active_match_ordinal, final_update);
263 }
264
265 base::WeakPtr<WebviewFindHelper::FindInfo>
266     WebviewFindHelper::FindInfo::AsWeakPtr() {
267   return weak_ptr_factory_.GetWeakPtr();
268 }
269
270 void WebviewFindHelper::FindInfo::SendResponse(bool canceled) {
271   // Prepare the find results to pass to the callback function.
272   base::DictionaryValue results;
273   find_results_.PrepareResults(&results);
274   results.SetBoolean(webview::kFindCanceled, canceled);
275
276   // Call the callback.
277   find_function_->SetResult(results.DeepCopy());
278   find_function_->SendResponse(true);
279 }