18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Copyright (c) 2012 The Chromium Authors. All rights reserved.
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Use of this source code is governed by a BSD-style license that can be
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// found in the LICENSE file.
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_tab_helper.h"
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <vector>
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/chrome_notification_types.h"
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/profiles/profile.h"
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_bar_state.h"
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/notification_service.h"
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/render_view_host.h"
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/browser/web_contents.h"
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "content/public/common/stop_find_action.h"
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "third_party/WebKit/public/web/WebFindOptions.h"
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "ui/gfx/rect_f.h"
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing blink::WebFindOptions;
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing content::WebContents;
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtDEFINE_WEB_CONTENTS_USER_DATA_KEY(FindTabHelper);
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// static
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FindTabHelper::find_request_id_counter_ = -1;
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtFindTabHelper::FindTabHelper(WebContents* web_contents)
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    : content::WebContentsObserver(web_contents),
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      find_ui_active_(false),
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      find_op_aborted_(false),
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      current_find_request_id_(find_request_id_counter_++),
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      last_search_case_sensitive_(false),
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      last_search_result_() {
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtFindTabHelper::~FindTabHelper() {
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::StartFinding(base::string16 search_string,
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                 bool forward_direction,
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                 bool case_sensitive) {
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Remove the carriage return character, which generally isn't in web content.
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  const base::char16 kInvalidChars[] = { '\r', 0 };
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  base::RemoveChars(search_string, kInvalidChars, &search_string);
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // If search_string is empty, it means FindNext was pressed with a keyboard
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // shortcut so unless we have something to search for we return early.
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (search_string.empty() && find_text_.empty()) {
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    Profile* profile =
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Profile::FromBrowserContext(web_contents()->GetBrowserContext());
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    base::string16 last_search_prepopulate_text =
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        FindBarStateFactory::GetLastPrepopulateText(profile);
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Try the last thing we searched for on this tab, then the last thing
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // searched for on any tab.
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (!previous_find_text_.empty())
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      search_string = previous_find_text_;
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    else if (!last_search_prepopulate_text.empty())
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      search_string = last_search_prepopulate_text;
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    else
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return;
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Keep track of the previous search.
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  previous_find_text_ = find_text_;
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // This is a FindNext operation if we are searching for the same text again,
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // or if the passed in search text is empty (FindNext keyboard shortcut). The
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // exception to this is if the Find was aborted (then we don't want FindNext
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // because the highlighting has been cleared and we need it to reappear). We
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // therefore treat FindNext after an aborted Find operation as a full fledged
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Find.
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  bool find_next = (find_text_ == search_string || search_string.empty()) &&
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                   (last_search_case_sensitive_ == case_sensitive) &&
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                   !find_op_aborted_;
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!find_next)
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    current_find_request_id_ = find_request_id_counter_++;
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!search_string.empty())
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    find_text_ = search_string;
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  last_search_case_sensitive_ = case_sensitive;
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  find_op_aborted_ = false;
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Keep track of what the last search was across the tabs.
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  Profile* profile =
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  FindBarState* find_bar_state = FindBarStateFactory::GetForProfile(profile);
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  find_bar_state->set_last_prepopulate_text(find_text_);
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  WebFindOptions options;
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  options.forward = forward_direction;
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  options.matchCase = case_sensitive;
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  options.findNext = find_next;
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  web_contents()->Find(current_find_request_id_, find_text_, options);
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::StopFinding(
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    FindBarController::SelectionAction selection_action) {
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (selection_action == FindBarController::kClearSelectionOnPage) {
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // kClearSelection means the find string has been cleared by the user, but
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // the UI has not been dismissed. In that case we want to clear the
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // previously remembered search (http://crbug.com/42639).
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    previous_find_text_ = base::string16();
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else {
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    find_ui_active_ = false;
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (!find_text_.empty())
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      previous_find_text_ = find_text_;
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  find_text_.clear();
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  find_op_aborted_ = true;
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  last_search_result_ = FindNotificationDetails();
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  content::StopFindAction action;
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (selection_action) {
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case FindBarController::kClearSelectionOnPage:
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      action = content::STOP_FIND_ACTION_CLEAR_SELECTION;
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case FindBarController::kKeepSelectionOnPage:
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      action = content::STOP_FIND_ACTION_KEEP_SELECTION;
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case FindBarController::kActivateSelectionOnPage:
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION;
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      action = content::STOP_FIND_ACTION_KEEP_SELECTION;
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  web_contents()->StopFinding(action);
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#if defined(OS_ANDROID)
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::ActivateNearestFindResult(float x, float y) {
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!find_op_aborted_ && !find_text_.empty()) {
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    web_contents()->GetRenderViewHost()->ActivateNearestFindResult(
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        current_find_request_id_, x, y);
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::RequestFindMatchRects(int current_version) {
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!find_op_aborted_ && !find_text_.empty())
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    web_contents()->GetRenderViewHost()->RequestFindMatchRects(current_version);
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#endif
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FindTabHelper::HandleFindReply(int request_id,
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                    int number_of_matches,
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                    const gfx::Rect& selection_rect,
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                    int active_match_ordinal,
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                    bool final_update) {
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Ignore responses for requests that have been aborted.
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Ignore responses for requests other than the one we have most recently
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // issued. That way we won't act on stale results when the user has
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // already typed in another query.
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!find_op_aborted_ && request_id == current_find_request_id_) {
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (number_of_matches == -1)
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      number_of_matches = last_search_result_.number_of_matches();
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (active_match_ordinal == -1)
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      active_match_ordinal = last_search_result_.active_match_ordinal();
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    gfx::Rect selection = selection_rect;
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (final_update && active_match_ordinal == 0)
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      selection = gfx::Rect();
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    else if (selection_rect.IsEmpty())
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      selection = last_search_result_.selection_rect();
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Notify the UI, automation and any other observers that a find result was
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // found.
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    last_search_result_ = FindNotificationDetails(
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        request_id, number_of_matches, selection, active_match_ordinal,
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        final_update);
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    content::NotificationService::current()->Notify(
1744530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt        chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
1754530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt        content::Source<WebContents>(web_contents()),
1764530cfd4d14a77c58e35393b91e40f8dd9d62697Dmitry Shmidt        content::Details<FindNotificationDetails>(&last_search_result_));
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt