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