• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/native/javascript_dialog_runner_win.h"
7 
8 #include "libcef/browser/alloy/alloy_browser_host_impl.h"
9 #include "libcef_dll/resource.h"
10 
11 #include "base/files/file_path.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 
16 class CefJavaScriptDialogRunnerWin;
17 
18 HHOOK CefJavaScriptDialogRunnerWin::msg_hook_ = NULL;
19 int CefJavaScriptDialogRunnerWin::msg_hook_user_count_ = 0;
20 
DialogProc(HWND dialog,UINT message,WPARAM wparam,LPARAM lparam)21 INT_PTR CALLBACK CefJavaScriptDialogRunnerWin::DialogProc(HWND dialog,
22                                                           UINT message,
23                                                           WPARAM wparam,
24                                                           LPARAM lparam) {
25   switch (message) {
26     case WM_INITDIALOG: {
27       SetWindowLongPtr(dialog, DWLP_USER, static_cast<LONG_PTR>(lparam));
28       CefJavaScriptDialogRunnerWin* owner =
29           reinterpret_cast<CefJavaScriptDialogRunnerWin*>(lparam);
30       owner->dialog_win_ = dialog;
31       SetDlgItemText(dialog, IDC_DIALOGTEXT, owner->message_text_.c_str());
32       if (owner->message_type_ == content::JAVASCRIPT_DIALOG_TYPE_PROMPT)
33         SetDlgItemText(dialog, IDC_PROMPTEDIT,
34                        owner->default_prompt_text_.c_str());
35       break;
36     }
37     case WM_CLOSE: {
38       CefJavaScriptDialogRunnerWin* owner =
39           reinterpret_cast<CefJavaScriptDialogRunnerWin*>(
40               GetWindowLongPtr(dialog, DWLP_USER));
41       if (owner) {
42         owner->CloseDialog(false, std::wstring());
43 
44         // No need for the system to call DestroyWindow() because it will be
45         // called by the Cancel() method.
46         return 0;
47       }
48       break;
49     }
50     case WM_COMMAND: {
51       CefJavaScriptDialogRunnerWin* owner =
52           reinterpret_cast<CefJavaScriptDialogRunnerWin*>(
53               GetWindowLongPtr(dialog, DWLP_USER));
54       std::wstring user_input;
55       bool finish = false;
56       bool result = false;
57       switch (LOWORD(wparam)) {
58         case IDOK:
59           finish = true;
60           result = true;
61           if (owner->message_type_ == content::JAVASCRIPT_DIALOG_TYPE_PROMPT) {
62             size_t length =
63                 GetWindowTextLength(GetDlgItem(dialog, IDC_PROMPTEDIT)) + 1;
64             if (length > 1) {
65               user_input.reserve(length);
66               user_input.resize(length - 1);
67               GetDlgItemText(dialog, IDC_PROMPTEDIT, &user_input[0], length);
68             }
69           }
70           break;
71         case IDCANCEL:
72           finish = true;
73           result = false;
74           break;
75       }
76       if (finish) {
77         owner->CloseDialog(result, user_input);
78       }
79       break;
80     }
81     default:
82       break;
83   }
84   return 0;
85 }
86 
CefJavaScriptDialogRunnerWin()87 CefJavaScriptDialogRunnerWin::CefJavaScriptDialogRunnerWin()
88     : dialog_win_(NULL), parent_win_(NULL), hook_installed_(false) {}
89 
~CefJavaScriptDialogRunnerWin()90 CefJavaScriptDialogRunnerWin::~CefJavaScriptDialogRunnerWin() {
91   Cancel();
92 }
93 
Run(AlloyBrowserHostImpl * browser,content::JavaScriptDialogType message_type,const std::u16string & display_url,const std::u16string & message_text,const std::u16string & default_prompt_text,DialogClosedCallback callback)94 void CefJavaScriptDialogRunnerWin::Run(
95     AlloyBrowserHostImpl* browser,
96     content::JavaScriptDialogType message_type,
97     const std::u16string& display_url,
98     const std::u16string& message_text,
99     const std::u16string& default_prompt_text,
100     DialogClosedCallback callback) {
101   DCHECK(!dialog_win_);
102 
103   message_type_ = message_type;
104   message_text_ = base::UTF16ToWide(message_text);
105   default_prompt_text_ = base::UTF16ToWide(default_prompt_text);
106   callback_ = std::move(callback);
107 
108   InstallMessageHook();
109   hook_installed_ = true;
110 
111   int dialog_type;
112   if (message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT)
113     dialog_type = IDD_ALERT;
114   else if (message_type == content::JAVASCRIPT_DIALOG_TYPE_CONFIRM)
115     dialog_type = IDD_CONFIRM;
116   else  // JAVASCRIPT_DIALOG_TYPE_PROMPT
117     dialog_type = IDD_PROMPT;
118 
119   base::FilePath file_path;
120   HMODULE hModule = NULL;
121 
122   // Try to load the dialog from the DLL.
123   if (base::PathService::Get(base::DIR_MODULE, &file_path)) {
124     file_path = file_path.Append(L"libcef.dll");
125     hModule = ::GetModuleHandle(file_path.value().c_str());
126   }
127   if (!hModule)
128     hModule = ::GetModuleHandle(NULL);
129   DCHECK(hModule);
130 
131   parent_win_ = GetAncestor(browser->GetWindowHandle(), GA_ROOT);
132   dialog_win_ =
133       CreateDialogParam(hModule, MAKEINTRESOURCE(dialog_type), parent_win_,
134                         DialogProc, reinterpret_cast<LPARAM>(this));
135   DCHECK(dialog_win_);
136 
137   if (!display_url.empty()) {
138     // Add the display URL to the window title.
139     wchar_t text[64];
140     GetWindowText(dialog_win_, text, sizeof(text) / sizeof(wchar_t));
141 
142     std::wstring new_window_text =
143         std::wstring(text) + L" - " + base::UTF16ToWide(display_url);
144     SetWindowText(dialog_win_, new_window_text.c_str());
145   }
146 
147   // Disable the parent window so the user can't interact with it.
148   if (IsWindowEnabled(parent_win_))
149     EnableWindow(parent_win_, FALSE);
150 
151   ShowWindow(dialog_win_, SW_SHOWNORMAL);
152 }
153 
Cancel()154 void CefJavaScriptDialogRunnerWin::Cancel() {
155   // Re-enable the parent before closing the popup to avoid focus/activation/
156   // z-order issues.
157   if (parent_win_ && IsWindow(parent_win_) && !IsWindowEnabled(parent_win_)) {
158     EnableWindow(parent_win_, TRUE);
159     parent_win_ = NULL;
160   }
161 
162   if (dialog_win_ && IsWindow(dialog_win_)) {
163     SetWindowLongPtr(dialog_win_, DWLP_USER, NULL);
164     DestroyWindow(dialog_win_);
165     dialog_win_ = NULL;
166   }
167 
168   if (hook_installed_) {
169     UninstallMessageHook();
170     hook_installed_ = false;
171   }
172 }
173 
CloseDialog(bool success,const std::wstring & user_input)174 void CefJavaScriptDialogRunnerWin::CloseDialog(bool success,
175                                                const std::wstring& user_input) {
176   // Run the callback first so that RenderProcessHostImpl::IsBlocked is
177   // cleared. Otherwise, RenderWidgetHostImpl::IsIgnoringInputEvents will
178   // return true and RenderWidgetHostViewAura::OnWindowFocused will fail to
179   // re-assign browser focus.
180   std::move(callback_).Run(success, base::WideToUTF16(user_input));
181   Cancel();
182 }
183 
184 // static
GetMsgProc(int code,WPARAM wparam,LPARAM lparam)185 LRESULT CALLBACK CefJavaScriptDialogRunnerWin::GetMsgProc(int code,
186                                                           WPARAM wparam,
187                                                           LPARAM lparam) {
188   // Mostly borrowed from http://support.microsoft.com/kb/q187988/
189   // and http://www.codeproject.com/KB/atl/cdialogmessagehook.aspx.
190   LPMSG msg = reinterpret_cast<LPMSG>(lparam);
191   if (code >= 0 && wparam == PM_REMOVE && msg->message >= WM_KEYFIRST &&
192       msg->message <= WM_KEYLAST) {
193     HWND hwnd = GetActiveWindow();
194     if (::IsWindow(hwnd) && ::IsDialogMessage(hwnd, msg)) {
195       // The value returned from this hookproc is ignored, and it cannot
196       // be used to tell Windows the message has been handled. To avoid
197       // further processing, convert the message to WM_NULL before
198       // returning.
199       msg->hwnd = NULL;
200       msg->message = WM_NULL;
201       msg->lParam = 0L;
202       msg->wParam = 0;
203     }
204   }
205 
206   // Passes the hook information to the next hook procedure in
207   // the current hook chain.
208   return ::CallNextHookEx(msg_hook_, code, wparam, lparam);
209 }
210 
211 // static
InstallMessageHook()212 bool CefJavaScriptDialogRunnerWin::InstallMessageHook() {
213   msg_hook_user_count_++;
214 
215   // Make sure we only call this once.
216   if (msg_hook_ != NULL)
217     return true;
218 
219   msg_hook_ = ::SetWindowsHookEx(WH_GETMESSAGE,
220                                  &CefJavaScriptDialogRunnerWin::GetMsgProc,
221                                  NULL, GetCurrentThreadId());
222   DCHECK(msg_hook_ != NULL);
223   return msg_hook_ != NULL;
224 }
225 
226 // static
UninstallMessageHook()227 bool CefJavaScriptDialogRunnerWin::UninstallMessageHook() {
228   msg_hook_user_count_--;
229   DCHECK_GE(msg_hook_user_count_, 0);
230 
231   if (msg_hook_user_count_ > 0)
232     return true;
233 
234   DCHECK(msg_hook_ != NULL);
235   BOOL result = ::UnhookWindowsHookEx(msg_hook_);
236   DCHECK(result);
237   msg_hook_ = NULL;
238 
239   return result != FALSE;
240 }
241