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 "extensions/browser/guest_view/web_view/web_view_find_helper.h" 6 7#include <utility> 8 9#include "extensions/browser/api/web_view/web_view_internal_api.h" 10#include "extensions/browser/guest_view/web_view/web_view_constants.h" 11 12namespace extensions { 13 14WebViewFindHelper::WebViewFindHelper(WebViewGuest* webview_guest) 15 : webview_guest_(webview_guest), current_find_request_id_(0) { 16} 17 18WebViewFindHelper::~WebViewFindHelper() { 19} 20 21void 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()); 26 } 27 if (find_update_event_.get()) 28 DispatchFindUpdateEvent(true /* canceled */, true /* final_update */); 29 find_update_event_.reset(); 30} 31 32void WebViewFindHelper::DispatchFindUpdateEvent(bool canceled, 33 bool final_update) { 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_->DispatchEventToEmbedder( 41 new GuestViewBase::Event(webview::kEventFindReply, args.Pass())); 42} 43 44void 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(); 49 50 // Call the callback function of the first request of the find session. 51 find_info->SendResponse(canceled); 52 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(); 57 ++i) { 58 DCHECK(i->get()); 59 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 65 // to be checked. 66 if (!(*i)->replied_) 67 break; 68 69 // Update the request's number of matches (if not canceled). 70 if (!canceled) { 71 (*i)->find_results_.number_of_matches_ = 72 find_info->find_results_.number_of_matches_; 73 } 74 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_); 79 } 80 81 // Erase the first find request's map entry to free the 82 // WebViewInternalFindFunction 83 // object. 84 find_info_map_.erase(session_request_id); 85} 86 87void 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_; 94 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_, 100 linked_ptr< 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); 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 136void 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 179WebViewFindHelper::FindResults::FindResults() 180 : number_of_matches_(0), active_match_ordinal_(0) { 181} 182 183WebViewFindHelper::FindResults::~FindResults() { 184} 185 186void 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 205void 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 217WebViewFindHelper::FindUpdateEvent::FindUpdateEvent( 218 const base::string16& search_text) 219 : search_text_(search_text) { 220} 221 222WebViewFindHelper::FindUpdateEvent::~FindUpdateEvent() { 223} 224 225void WebViewFindHelper::FindUpdateEvent::AggregateResults( 226 int number_of_matches, 227 const gfx::Rect& selection_rect, 228 int active_match_ordinal, 229 bool final_update) { 230 find_results_.AggregateResults(number_of_matches, selection_rect, 231 active_match_ordinal, final_update); 232} 233 234void WebViewFindHelper::FindUpdateEvent::PrepareResults( 235 base::DictionaryValue* results) { 236 results->SetString(webview::kFindSearchText, search_text_); 237 find_results_.PrepareResults(results); 238} 239 240WebViewFindHelper::FindInfo::FindInfo( 241 int request_id, 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), 247 options_(options), 248 find_function_(find_function), 249 replied_(false), 250 weak_ptr_factory_(this) { 251} 252 253WebViewFindHelper::FindInfo::~FindInfo() { 254} 255 256void WebViewFindHelper::FindInfo::AggregateResults( 257 int number_of_matches, 258 const gfx::Rect& selection_rect, 259 int active_match_ordinal, 260 bool final_update) { 261 replied_ = true; 262 find_results_.AggregateResults(number_of_matches, selection_rect, 263 active_match_ordinal, final_update); 264} 265 266base::WeakPtr<WebViewFindHelper::FindInfo> 267WebViewFindHelper::FindInfo::AsWeakPtr() { 268 return weak_ptr_factory_.GetWeakPtr(); 269} 270 271void 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); 276 277 // Call the callback. 278 find_function_->SetResult(results.DeepCopy()); 279 find_function_->SendResponse(true); 280} 281 282} // namespace extensions 283