Home | History | Annotate | Download | only in chrome_frame
      1 // Copyright (c) 2012 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_frame/find_dialog.h"
      6 
      7 #include <richedit.h>
      8 
      9 #include "base/guid.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome_frame/chrome_frame_automation.h"
     12 
     13 const int kMaxFindChars = 1024;
     14 
     15 HHOOK CFFindDialog::msg_hook_ = NULL;
     16 
     17 CFFindDialog::CFFindDialog() {}
     18 
     19 void CFFindDialog::Init(ChromeFrameAutomationClient* automation_client) {
     20   automation_client_ = automation_client;
     21 }
     22 
     23 LRESULT CFFindDialog::OnDestroy(UINT msg, WPARAM wparam, LPARAM lparam,
     24                                 BOOL& handled) {
     25   // In order to cancel the selection when the Find dialog is dismissed, we
     26   // do a fake search for a string that is unlikely to appear on the page.
     27   // TODO(robertshield): Change this to plumb through a StopFinding automation
     28   // message that triggers a ViewMsg_StopFinding.
     29   std::string guid(base::GenerateGUID());
     30   automation_client_->FindInPage(ASCIIToWide(guid), FWD, CASE_SENSITIVE, false);
     31 
     32   UninstallMessageHook();
     33   return 0;
     34 }
     35 
     36 LRESULT CFFindDialog::OnFind(WORD wNotifyCode, WORD wID, HWND hWndCtl,
     37                              BOOL& bHandled) {
     38   string16 find_text(kMaxFindChars, L'\0');
     39   find_text.resize(GetDlgItemText(IDC_FIND_TEXT, &find_text[0], kMaxFindChars));
     40 
     41   // Repeated searches for the same string should move to the next instance.
     42   bool find_next = (find_text == last_find_text_);
     43   if (!find_next)
     44     last_find_text_ = find_text;
     45 
     46   bool match_case = IsDlgButtonChecked(IDC_MATCH_CASE) == BST_CHECKED;
     47   bool search_down = IsDlgButtonChecked(IDC_DIRECTION_DOWN) == BST_CHECKED;
     48 
     49   automation_client_->FindInPage(find_text,
     50                                  search_down ? FWD : BACK,
     51                                  match_case ? CASE_SENSITIVE : IGNORE_CASE,
     52                                  find_next);
     53 
     54   return 0;
     55 }
     56 
     57 LRESULT CFFindDialog::OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl,
     58                                BOOL& bHandled) {
     59   DestroyWindow();
     60   return 0;
     61 }
     62 
     63 LRESULT CFFindDialog::OnInitDialog(UINT msg, WPARAM wparam, LPARAM lparam,
     64                                    BOOL& handled) {
     65   // Init() must be called before Create() or DoModal()!
     66   DCHECK(automation_client_.get());
     67 
     68   InstallMessageHook();
     69   SendDlgItemMessage(IDC_FIND_TEXT, EM_EXLIMITTEXT, 0, kMaxFindChars);
     70   BOOL result = CheckRadioButton(IDC_DIRECTION_DOWN, IDC_DIRECTION_UP,
     71                                  IDC_DIRECTION_DOWN);
     72 
     73   HWND text_field = GetDlgItem(IDC_FIND_TEXT);
     74   ::SetFocus(text_field);
     75 
     76   return FALSE;  // we set the focus ourselves.
     77 }
     78 
     79 LRESULT CALLBACK CFFindDialog::GetMsgProc(int code, WPARAM wparam,
     80                                           LPARAM lparam) {
     81   // Mostly borrowed from http://support.microsoft.com/kb/q187988/
     82   // and http://www.codeproject.com/KB/atl/cdialogmessagehook.aspx.
     83   LPMSG msg = reinterpret_cast<LPMSG>(lparam);
     84   if (code >= 0 && wparam == PM_REMOVE &&
     85       msg->message >= WM_KEYFIRST && msg->message <= WM_KEYLAST) {
     86     HWND hwnd = GetActiveWindow();
     87     if (::IsWindow(hwnd) && ::IsDialogMessage(hwnd, msg)) {
     88       // The value returned from this hookproc is ignored, and it cannot
     89       // be used to tell Windows the message has been handled. To avoid
     90       // further processing, convert the message to WM_NULL before
     91       // returning.
     92       msg->hwnd = NULL;
     93       msg->message = WM_NULL;
     94       msg->lParam = 0L;
     95       msg->wParam = 0;
     96     }
     97   }
     98 
     99   // Passes the hook information to the next hook procedure in
    100   // the current hook chain.
    101   return ::CallNextHookEx(msg_hook_, code, wparam, lparam);
    102 }
    103 
    104 bool CFFindDialog::InstallMessageHook() {
    105   // Make sure we only call this once.
    106   DCHECK(msg_hook_ == NULL);
    107   msg_hook_ = ::SetWindowsHookEx(WH_GETMESSAGE, &CFFindDialog::GetMsgProc,
    108                                  _AtlBaseModule.m_hInst, GetCurrentThreadId());
    109   DCHECK(msg_hook_ != NULL);
    110   return msg_hook_ != NULL;
    111 }
    112 
    113 bool CFFindDialog::UninstallMessageHook() {
    114   DCHECK(msg_hook_ != NULL);
    115   BOOL result = ::UnhookWindowsHookEx(msg_hook_);
    116   DCHECK(result);
    117   msg_hook_ = NULL;
    118 
    119   return result != FALSE;
    120 }
    121