Home | History | Annotate | Download | only in find_bar
      1 // Copyright (c) 2011 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/ui/find_bar/find_tab_helper.h"
      6 
      7 #include <vector>
      8 
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/ui/find_bar/find_bar_state.h"
     11 #include "content/browser/renderer_host/render_view_host.h"
     12 #include "content/browser/tab_contents/tab_contents.h"
     13 #include "content/common/notification_service.h"
     14 #include "content/common/view_messages.h"
     15 
     16 // static
     17 int FindTabHelper::find_request_id_counter_ = -1;
     18 
     19 FindTabHelper::FindTabHelper(TabContents* tab_contents)
     20     : TabContentsObserver(tab_contents),
     21       find_ui_active_(false),
     22       find_op_aborted_(false),
     23       current_find_request_id_(find_request_id_counter_++),
     24       last_search_case_sensitive_(false),
     25       last_search_result_() {
     26 }
     27 
     28 FindTabHelper::~FindTabHelper() {
     29 }
     30 
     31 void FindTabHelper::StartFinding(string16 search_string,
     32                                  bool forward_direction,
     33                                  bool case_sensitive) {
     34   // If search_string is empty, it means FindNext was pressed with a keyboard
     35   // shortcut so unless we have something to search for we return early.
     36   if (search_string.empty() && find_text_.empty()) {
     37     string16 last_search_prepopulate_text =
     38         FindBarState::GetLastPrepopulateText(tab_contents()->profile());
     39 
     40     // Try the last thing we searched for on this tab, then the last thing
     41     // searched for on any tab.
     42     if (!previous_find_text_.empty())
     43       search_string = previous_find_text_;
     44     else if (!last_search_prepopulate_text.empty())
     45       search_string = last_search_prepopulate_text;
     46     else
     47       return;
     48   }
     49 
     50   // Keep track of the previous search.
     51   previous_find_text_ = find_text_;
     52 
     53   // This is a FindNext operation if we are searching for the same text again,
     54   // or if the passed in search text is empty (FindNext keyboard shortcut). The
     55   // exception to this is if the Find was aborted (then we don't want FindNext
     56   // because the highlighting has been cleared and we need it to reappear). We
     57   // therefore treat FindNext after an aborted Find operation as a full fledged
     58   // Find.
     59   bool find_next = (find_text_ == search_string || search_string.empty()) &&
     60                    (last_search_case_sensitive_ == case_sensitive) &&
     61                    !find_op_aborted_;
     62   if (!find_next)
     63     current_find_request_id_ = find_request_id_counter_++;
     64 
     65   if (!search_string.empty())
     66     find_text_ = search_string;
     67   last_search_case_sensitive_ = case_sensitive;
     68 
     69   find_op_aborted_ = false;
     70 
     71   // Keep track of what the last search was across the tabs.
     72   FindBarState* find_bar_state = tab_contents()->profile()->GetFindBarState();
     73   find_bar_state->set_last_prepopulate_text(find_text_);
     74   tab_contents()->render_view_host()->StartFinding(current_find_request_id_,
     75                                                    find_text_,
     76                                                    forward_direction,
     77                                                    case_sensitive,
     78                                                    find_next);
     79 }
     80 
     81 void FindTabHelper::StopFinding(
     82     FindBarController::SelectionAction selection_action) {
     83   if (selection_action == FindBarController::kClearSelection) {
     84     // kClearSelection means the find string has been cleared by the user, but
     85     // the UI has not been dismissed. In that case we want to clear the
     86     // previously remembered search (http://crbug.com/42639).
     87     previous_find_text_ = string16();
     88   } else {
     89     find_ui_active_ = false;
     90     if (!find_text_.empty())
     91       previous_find_text_ = find_text_;
     92   }
     93   find_text_.clear();
     94   find_op_aborted_ = true;
     95   last_search_result_ = FindNotificationDetails();
     96   tab_contents()->render_view_host()->StopFinding(selection_action);
     97 }
     98 
     99 bool FindTabHelper::OnMessageReceived(const IPC::Message& message) {
    100   bool handled = true;
    101   IPC_BEGIN_MESSAGE_MAP(FindTabHelper, message)
    102     IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnFindReply)
    103     IPC_MESSAGE_UNHANDLED(handled = false)
    104   IPC_END_MESSAGE_MAP()
    105   return handled;
    106 }
    107 
    108 void FindTabHelper::OnFindReply(int request_id,
    109                                 int number_of_matches,
    110                                 const gfx::Rect& selection_rect,
    111                                 int active_match_ordinal,
    112                                 bool final_update) {
    113   // Ignore responses for requests that have been aborted.
    114   // Ignore responses for requests other than the one we have most recently
    115   // issued. That way we won't act on stale results when the user has
    116   // already typed in another query.
    117   if (!find_op_aborted_ && request_id == current_find_request_id_) {
    118     if (number_of_matches == -1)
    119       number_of_matches = last_search_result_.number_of_matches();
    120     if (active_match_ordinal == -1)
    121       active_match_ordinal = last_search_result_.active_match_ordinal();
    122 
    123     gfx::Rect selection = selection_rect;
    124     if (selection.IsEmpty())
    125       selection = last_search_result_.selection_rect();
    126 
    127     // Notify the UI, automation and any other observers that a find result was
    128     // found.
    129     last_search_result_ = FindNotificationDetails(
    130         request_id, number_of_matches, selection, active_match_ordinal,
    131         final_update);
    132     NotificationService::current()->Notify(
    133         NotificationType::FIND_RESULT_AVAILABLE,
    134         Source<TabContents>(tab_contents()),
    135         Details<FindNotificationDetails>(&last_search_result_));
    136   }
    137 
    138   // Send a notification to the renderer that we are ready to receive more
    139   // results from the scoping effort of the Find operation. The FindInPage
    140   // scoping is asynchronous and periodically sends results back up to the
    141   // browser using IPC. In an effort to not spam the browser we have the
    142   // browser send an ACK for each FindReply message and have the renderer
    143   // queue up the latest status message while waiting for this ACK.
    144   Send(new ViewMsg_FindReplyACK(routing_id()));
    145 }
    146