| Index: chrome/browser/guest_view/web_view/web_view_find_helper.cc | 
| diff --git a/chrome/browser/guest_view/web_view/web_view_find_helper.cc b/chrome/browser/guest_view/web_view/web_view_find_helper.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..16d5a92c767c4c576696520ca76cdb7c8e084094 | 
| --- /dev/null | 
| +++ b/chrome/browser/guest_view/web_view/web_view_find_helper.cc | 
| @@ -0,0 +1,282 @@ | 
| +// Copyright 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "chrome/browser/guest_view/web_view/web_view_find_helper.h" | 
| + | 
| +#include <utility> | 
| + | 
| +#include "chrome/browser/extensions/api/web_view/web_view_internal_api.h" | 
| +#include "chrome/browser/guest_view/web_view/web_view_constants.h" | 
| + | 
| +namespace extensions { | 
| + | 
| +WebViewFindHelper::WebViewFindHelper(WebViewGuest* webview_guest) | 
| +    : webview_guest_(webview_guest), current_find_request_id_(0) { | 
| +} | 
| + | 
| +WebViewFindHelper::~WebViewFindHelper() { | 
| +} | 
| + | 
| +void WebViewFindHelper::CancelAllFindSessions() { | 
| +  current_find_session_ = linked_ptr<WebViewFindHelper::FindInfo>(); | 
| +  while (!find_info_map_.empty()) { | 
| +    find_info_map_.begin()->second->SendResponse(true /* canceled */); | 
| +    find_info_map_.erase(find_info_map_.begin()); | 
| +  } | 
| +  if (find_update_event_.get()) | 
| +    DispatchFindUpdateEvent(true /* canceled */, true /* final_update */); | 
| +  find_update_event_.reset(); | 
| +} | 
| + | 
| +void WebViewFindHelper::DispatchFindUpdateEvent(bool canceled, | 
| +                                                bool final_update) { | 
| +  DCHECK(find_update_event_.get()); | 
| +  scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | 
| +  find_update_event_->PrepareResults(args.get()); | 
| +  args->SetBoolean(webview::kFindCanceled, canceled); | 
| +  args->SetBoolean(webview::kFindFinalUpdate, final_update); | 
| +  DCHECK(webview_guest_); | 
| +  webview_guest_->DispatchEventToEmbedder( | 
| +      new GuestViewBase::Event(webview::kEventFindReply, args.Pass())); | 
| +} | 
| + | 
| +void WebViewFindHelper::EndFindSession(int session_request_id, bool canceled) { | 
| +  FindInfoMap::iterator session_iterator = | 
| +      find_info_map_.find(session_request_id); | 
| +  DCHECK(session_iterator != find_info_map_.end()); | 
| +  FindInfo* find_info = session_iterator->second.get(); | 
| + | 
| +  // Call the callback function of the first request of the find session. | 
| +  find_info->SendResponse(canceled); | 
| + | 
| +  // For every subsequent find request of the find session. | 
| +  for (std::vector<base::WeakPtr<WebViewFindHelper::FindInfo> >::iterator i = | 
| +           find_info->find_next_requests_.begin(); | 
| +       i != find_info->find_next_requests_.end(); | 
| +       ++i) { | 
| +    DCHECK(i->get()); | 
| + | 
| +    // Do not call callbacks for subsequent find requests that have not been | 
| +    // replied to yet. These requests will get their own final updates in the | 
| +    // same order as they appear in |find_next_requests_|, i.e. the order that | 
| +    // the requests were made in. Once one request is found that has not been | 
| +    // replied to, none that follow will be replied to either, and do not need | 
| +    // to be checked. | 
| +    if (!(*i)->replied_) | 
| +      break; | 
| + | 
| +    // Update the request's number of matches (if not canceled). | 
| +    if (!canceled) { | 
| +      (*i)->find_results_.number_of_matches_ = | 
| +          find_info->find_results_.number_of_matches_; | 
| +    } | 
| + | 
| +    // Call the request's callback function with the find results, and then | 
| +    // delete its map entry to free the WebViewInternalFindFunction object. | 
| +    (*i)->SendResponse(canceled); | 
| +    find_info_map_.erase((*i)->request_id_); | 
| +  } | 
| + | 
| +  // Erase the first find request's map entry to free the | 
| +  // WebViewInternalFindFunction | 
| +  // object. | 
| +  find_info_map_.erase(session_request_id); | 
| +} | 
| + | 
| +void WebViewFindHelper::Find( | 
| +    content::WebContents* guest_web_contents, | 
| +    const base::string16& search_text, | 
| +    const blink::WebFindOptions& options, | 
| +    scoped_refptr<WebViewInternalFindFunction> find_function) { | 
| +  // Need a new request_id for each new find request. | 
| +  ++current_find_request_id_; | 
| + | 
| +  // Stores the find request information by request_id so that its callback | 
| +  // function can be called when the find results are available. | 
| +  std::pair<FindInfoMap::iterator, bool> insert_result = | 
| +      find_info_map_.insert(std::make_pair( | 
| +          current_find_request_id_, | 
| +          linked_ptr< | 
| +              WebViewFindHelper::FindInfo>(new WebViewFindHelper::FindInfo( | 
| +              current_find_request_id_, search_text, options, find_function)))); | 
| +  // No duplicate insertions. | 
| +  DCHECK(insert_result.second); | 
| + | 
| +  // Find options including the implicit |findNext| field. | 
| +  blink::WebFindOptions* full_options = insert_result.first->second->options(); | 
| + | 
| +  // Set |findNext| implicitly. | 
| +  if (current_find_session_.get()) { | 
| +    const base::string16& current_search_text = | 
| +        current_find_session_->search_text(); | 
| +    bool current_match_case = current_find_session_->options()->matchCase; | 
| +    full_options->findNext = !current_search_text.empty() && | 
| +        current_search_text == search_text && | 
| +        current_match_case == options.matchCase; | 
| +  } else { | 
| +    full_options->findNext = false; | 
| +  } | 
| + | 
| +  // Link find requests that are a part of the same find session. | 
| +  if (full_options->findNext && current_find_session_.get()) { | 
| +    DCHECK(current_find_request_id_ != current_find_session_->request_id()); | 
| +    current_find_session_->AddFindNextRequest( | 
| +        insert_result.first->second->AsWeakPtr()); | 
| +  } | 
| + | 
| +  // Update the current find session, if necessary. | 
| +  if (!full_options->findNext) | 
| +    current_find_session_ = insert_result.first->second; | 
| + | 
| +  guest_web_contents->Find(current_find_request_id_, | 
| +                           search_text, *full_options); | 
| +} | 
| + | 
| +void WebViewFindHelper::FindReply(int request_id, | 
| +                                  int number_of_matches, | 
| +                                  const gfx::Rect& selection_rect, | 
| +                                  int active_match_ordinal, | 
| +                                  bool final_update) { | 
| +  FindInfoMap::iterator find_iterator = find_info_map_.find(request_id); | 
| + | 
| +  // Ignore slow replies to canceled find requests. | 
| +  if (find_iterator == find_info_map_.end()) | 
| +    return; | 
| + | 
| +  // This find request must be a part of an existing find session. | 
| +  DCHECK(current_find_session_.get()); | 
| + | 
| +  WebViewFindHelper::FindInfo* find_info = find_iterator->second.get(); | 
| + | 
| +  // Handle canceled find requests. | 
| +  if (!find_info->options()->findNext && | 
| +      find_info_map_.begin()->first < request_id) { | 
| +    DCHECK_NE(current_find_session_->request_id(), | 
| +              find_info_map_.begin()->first); | 
| +    DispatchFindUpdateEvent(true /* canceled */, true /* final_update */); | 
| +    EndFindSession(find_info_map_.begin()->first, true /* canceled */); | 
| +  } | 
| + | 
| +  // Clears the results for |findupdate| for a new find session. | 
| +  if (!find_info->replied() && !find_info->options()->findNext) | 
| +    find_update_event_.reset(new FindUpdateEvent(find_info->search_text())); | 
| + | 
| +  // Aggregate the find results. | 
| +  find_info->AggregateResults(number_of_matches, selection_rect, | 
| +                              active_match_ordinal, final_update); | 
| +  find_update_event_->AggregateResults(number_of_matches, selection_rect, | 
| +                                      active_match_ordinal, final_update); | 
| + | 
| +  // Propagate incremental results to the |findupdate| event. | 
| +  DispatchFindUpdateEvent(false /* canceled */, final_update); | 
| + | 
| +  // Call the callback functions of completed find requests. | 
| +  if (final_update) | 
| +    EndFindSession(request_id, false /* canceled */); | 
| +} | 
| + | 
| +WebViewFindHelper::FindResults::FindResults() | 
| +    : number_of_matches_(0), active_match_ordinal_(0) { | 
| +} | 
| + | 
| +WebViewFindHelper::FindResults::~FindResults() { | 
| +} | 
| + | 
| +void WebViewFindHelper::FindResults::AggregateResults( | 
| +    int number_of_matches, | 
| +    const gfx::Rect& selection_rect, | 
| +    int active_match_ordinal, | 
| +    bool final_update) { | 
| +  if (number_of_matches != -1) | 
| +    number_of_matches_ = number_of_matches; | 
| + | 
| +  if (active_match_ordinal != -1) | 
| +    active_match_ordinal_ = active_match_ordinal; | 
| + | 
| +  if (final_update && active_match_ordinal_ == 0) { | 
| +    // No match found, so the selection rectangle is empty. | 
| +    selection_rect_ = gfx::Rect(); | 
| +  } else if (!selection_rect.IsEmpty()) { | 
| +    selection_rect_ = selection_rect; | 
| +  } | 
| +} | 
| + | 
| +void WebViewFindHelper::FindResults::PrepareResults( | 
| +    base::DictionaryValue* results) { | 
| +  results->SetInteger(webview::kFindNumberOfMatches, number_of_matches_); | 
| +  results->SetInteger(webview::kFindActiveMatchOrdinal, active_match_ordinal_); | 
| +  base::DictionaryValue rect; | 
| +  rect.SetInteger(webview::kFindRectLeft, selection_rect_.x()); | 
| +  rect.SetInteger(webview::kFindRectTop, selection_rect_.y()); | 
| +  rect.SetInteger(webview::kFindRectWidth, selection_rect_.width()); | 
| +  rect.SetInteger(webview::kFindRectHeight, selection_rect_.height()); | 
| +  results->Set(webview::kFindSelectionRect, rect.DeepCopy()); | 
| +} | 
| + | 
| +WebViewFindHelper::FindUpdateEvent::FindUpdateEvent( | 
| +    const base::string16& search_text) | 
| +    : search_text_(search_text) { | 
| +} | 
| + | 
| +WebViewFindHelper::FindUpdateEvent::~FindUpdateEvent() { | 
| +} | 
| + | 
| +void WebViewFindHelper::FindUpdateEvent::AggregateResults( | 
| +    int number_of_matches, | 
| +    const gfx::Rect& selection_rect, | 
| +    int active_match_ordinal, | 
| +    bool final_update) { | 
| +  find_results_.AggregateResults(number_of_matches, selection_rect, | 
| +                                 active_match_ordinal, final_update); | 
| +} | 
| + | 
| +void WebViewFindHelper::FindUpdateEvent::PrepareResults( | 
| +    base::DictionaryValue* results) { | 
| +  results->SetString(webview::kFindSearchText, search_text_); | 
| +  find_results_.PrepareResults(results); | 
| +} | 
| + | 
| +WebViewFindHelper::FindInfo::FindInfo( | 
| +    int request_id, | 
| +    const base::string16& search_text, | 
| +    const blink::WebFindOptions& options, | 
| +    scoped_refptr<WebViewInternalFindFunction> find_function) | 
| +    : request_id_(request_id), | 
| +      search_text_(search_text), | 
| +      options_(options), | 
| +      find_function_(find_function), | 
| +      replied_(false), | 
| +      weak_ptr_factory_(this) { | 
| +} | 
| + | 
| +WebViewFindHelper::FindInfo::~FindInfo() { | 
| +} | 
| + | 
| +void WebViewFindHelper::FindInfo::AggregateResults( | 
| +    int number_of_matches, | 
| +    const gfx::Rect& selection_rect, | 
| +    int active_match_ordinal, | 
| +    bool final_update) { | 
| +  replied_ = true; | 
| +  find_results_.AggregateResults(number_of_matches, selection_rect, | 
| +                                 active_match_ordinal, final_update); | 
| +} | 
| + | 
| +base::WeakPtr<WebViewFindHelper::FindInfo> | 
| +WebViewFindHelper::FindInfo::AsWeakPtr() { | 
| +  return weak_ptr_factory_.GetWeakPtr(); | 
| +} | 
| + | 
| +void WebViewFindHelper::FindInfo::SendResponse(bool canceled) { | 
| +  // Prepare the find results to pass to the callback function. | 
| +  base::DictionaryValue results; | 
| +  find_results_.PrepareResults(&results); | 
| +  results.SetBoolean(webview::kFindCanceled, canceled); | 
| + | 
| +  // Call the callback. | 
| +  find_function_->SetResult(results.DeepCopy()); | 
| +  find_function_->SendResponse(true); | 
| +} | 
| + | 
| +}  // namespace extensions | 
|  |