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.
9 #include "base/win/scoped_variant.h"
10 #include "chrome/installer/util/html_dialog.h"
12 #pragma comment(lib, "urlmon.lib")
16 // Windows implementation of the HTML dialog class. The main danger with
17 // using the IE embedded control as a child window of a custom window is that
18 // it still contains too much browser functionality, allowing the user to do
19 // things that are not expected of a plain dialog. ShowHTMLDialog API solves
20 // that problem but gives us a not very customizable frame. We solve that
21 // using hooks to end up with a robust dialog at the expense of having to do
22 // the buttons in html itself, like so:
24 // <form onsubmit="submit_it(this); return false;">
25 // <input name="accept" type="checkbox" /> My cool option
26 // <input name="submit" type="submit" value="[accept]" />
29 // function submit_it(f) {
30 // if (f.accept.checked) {
31 // window.returnValue = 1; <-- this matches HTML_DLG_ACCEPT
33 // window.returnValue = 2; <-- this matches HTML_DLG_DECLINE
38 // Note that on the submit handler you need to set window.returnValue to one of
39 // the values of DialogResult and call window.close().
41 class HTMLDialogWin
: public HTMLDialog
{
43 HTMLDialogWin(const base::string16
& url
, const base::string16
& param
)
44 : url_(url
), param_(param
) {
46 mshtml_
= LoadLibrary(L
"MSHTML.DLL");
49 DialogResult
ShowModal(void* parent_window
,
50 CustomizationCallback
* callback
) override
{
51 int result
= HTML_DLG_DECLINE
;
52 if (!InternalDoDialog(callback
, &result
))
53 return HTML_DLG_ERROR
;
54 return static_cast<DialogResult
>(result
);
57 base::string16
GetExtraResult() override
{ return extra_result_
; }
60 bool InternalDoDialog(CustomizationCallback
* callback
, int* result
);
61 static LRESULT CALLBACK
MsgFilter(int code
, WPARAM wParam
, LPARAM lParam
);
64 base::string16 param_
;
66 static HINSTANCE mshtml_
;
67 static CustomizationCallback
* callback_
;
68 base::string16 extra_result_
;
71 HTMLDialog
* CreateNativeHTMLDialog(const base::string16
& url
,
72 const base::string16
& param
) {
73 return new HTMLDialogWin(url
, param
);
76 HHOOK
HTMLDialogWin::hook_
= NULL
;
77 HINSTANCE
HTMLDialogWin::mshtml_
= NULL
;
78 HTMLDialogWin::CustomizationCallback
* HTMLDialogWin::callback_
= NULL
;
80 // This hook function gets called for messages bound to the windows that
81 // ShowHTMLDialog creates. We tell apart the top window because it has the
83 LRESULT
HTMLDialogWin::MsgFilter(int code
, WPARAM wParam
, LPARAM lParam
) {
84 static bool tweak_window
= true;
85 if (lParam
&& tweak_window
) {
86 HWND target_window
= reinterpret_cast<MSG
*>(lParam
)->hwnd
;
88 LONG_PTR style
= ::GetWindowLongPtrW(target_window
, GWL_STYLE
);
89 if (style
& WS_SYSMENU
) {
91 callback_
->OnBeforeDisplay(target_window
);
95 // Always call the next hook in the chain.
96 return ::CallNextHookEx(hook_
, code
, wParam
, lParam
);
99 bool HTMLDialogWin::InternalDoDialog(CustomizationCallback
* callback
,
103 SHOWHTMLDIALOGFN
* show_html_dialog
=
104 reinterpret_cast<SHOWHTMLDIALOGFN
*>(
105 GetProcAddress(mshtml_
, "ShowHTMLDialog"));
106 if (!show_html_dialog
)
109 IMoniker
*url_moniker
= NULL
;
110 ::CreateURLMonikerEx(NULL
, url_
.c_str(), &url_moniker
, URL_MK_UNIFORM
);
114 wchar_t* extra_args
= NULL
;
116 callback
->OnBeforeCreation(&extra_args
);
117 // Sets a windows hook for this thread only.
118 hook_
= ::SetWindowsHookEx(WH_GETMESSAGE
, MsgFilter
, NULL
,
119 GetCurrentThreadId());
121 callback_
= callback
;
124 // Pass our parameter to the dialog in the dialogArguments property of
125 // the window object.
126 base::win::ScopedVariant
dialog_args(param_
.c_str());
129 ::VariantInit(&v_result
);
131 // Creates the window with the embedded IE control in a modal loop.
132 HRESULT hr
= show_html_dialog(NULL
,
134 dialog_args
.AsInput(),
137 url_moniker
->Release();
139 if (v_result
.vt
== VT_I4
) {
140 *result
= v_result
.intVal
;
141 } else if (v_result
.vt
== VT_BSTR
) {
142 *result
= HTML_DLG_EXTRA
;
143 extra_result_
.assign(v_result
.bstrVal
, SysStringLen(v_result
.bstrVal
));
146 ::VariantClear(&v_result
);
149 ::UnhookWindowsHookEx(hook_
);
153 return SUCCEEDED(hr
);
156 // EulaHTMLDialog implementation ---------------------------------------------
158 void EulaHTMLDialog::Customizer::OnBeforeCreation(wchar_t** extra
) {
161 // The customization of the window consists in removing the close button and
162 // replacing the existing 'e' icon with the standard informational icon.
163 void EulaHTMLDialog::Customizer::OnBeforeDisplay(void* window
) {
166 HWND top_window
= static_cast<HWND
>(window
);
167 LONG_PTR style
= ::GetWindowLongPtrW(top_window
, GWL_STYLE
);
168 ::SetWindowLongPtrW(top_window
, GWL_STYLE
, style
& ~WS_SYSMENU
);
169 HICON ico
= ::LoadIcon(NULL
, IDI_INFORMATION
);
170 ::SendMessageW(top_window
, WM_SETICON
, ICON_SMALL
,
171 reinterpret_cast<LPARAM
>(ico
));
174 EulaHTMLDialog::EulaHTMLDialog(const base::string16
& file
,
175 const base::string16
& param
) {
176 dialog_
= CreateNativeHTMLDialog(file
, param
);
179 EulaHTMLDialog::~EulaHTMLDialog() {
183 EulaHTMLDialog::Outcome
EulaHTMLDialog::ShowModal() {
184 Customizer customizer
;
185 HTMLDialog::DialogResult dr
= dialog_
->ShowModal(NULL
, &customizer
);
186 if (HTMLDialog::HTML_DLG_ACCEPT
== dr
)
187 return EulaHTMLDialog::ACCEPTED
;
188 else if (HTMLDialog::HTML_DLG_EXTRA
== dr
)
189 return EulaHTMLDialog::ACCEPTED_OPT_IN
;
191 return EulaHTMLDialog::REJECTED
;
194 } // namespace installer