Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / installer / util / html_dialog_impl.cc
blob73763596def30a54885b6d8e5d0a7b8c919b1366
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.
5 #include <windows.h>
6 #include <mshtmhst.h>
7 #include <urlmon.h>
9 #include "base/win/scoped_variant.h"
10 #include "chrome/installer/util/html_dialog.h"
12 #pragma comment(lib, "urlmon.lib")
14 namespace installer {
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]" />
27 // </form>
29 // function submit_it(f) {
30 // if (f.accept.checked) {
31 // window.returnValue = 1; <-- this matches HTML_DLG_ACCEPT
32 // } else {
33 // window.returnValue = 2; <-- this matches HTML_DLG_DECLINE
34 // }
35 // window.close();
36 // }
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 {
42 public:
43 HTMLDialogWin(const base::string16& url, const base::string16& param)
44 : url_(url), param_(param) {
45 if (!mshtml_)
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_; }
59 private:
60 bool InternalDoDialog(CustomizationCallback* callback, int* result);
61 static LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);
63 base::string16 url_;
64 base::string16 param_;
65 static HHOOK hook_;
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
82 // system menu style.
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;
87 if (target_window) {
88 LONG_PTR style = ::GetWindowLongPtrW(target_window, GWL_STYLE);
89 if (style & WS_SYSMENU) {
90 tweak_window = false;
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,
100 int* result) {
101 if (!mshtml_)
102 return false;
103 SHOWHTMLDIALOGFN* show_html_dialog =
104 reinterpret_cast<SHOWHTMLDIALOGFN*>(
105 GetProcAddress(mshtml_, "ShowHTMLDialog"));
106 if (!show_html_dialog)
107 return false;
109 IMoniker *url_moniker = NULL;
110 ::CreateURLMonikerEx(NULL, url_.c_str(), &url_moniker, URL_MK_UNIFORM);
111 if (!url_moniker)
112 return false;
114 wchar_t* extra_args = NULL;
115 if (callback) {
116 callback->OnBeforeCreation(&extra_args);
117 // Sets a windows hook for this thread only.
118 hook_ = ::SetWindowsHookEx(WH_GETMESSAGE, MsgFilter, NULL,
119 GetCurrentThreadId());
120 if (hook_)
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());
128 VARIANT v_result;
129 ::VariantInit(&v_result);
131 // Creates the window with the embedded IE control in a modal loop.
132 HRESULT hr = show_html_dialog(NULL,
133 url_moniker,
134 dialog_args.AsInput(),
135 extra_args,
136 &v_result);
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);
148 if (hook_) {
149 ::UnhookWindowsHookEx(hook_);
150 callback_ = NULL;
151 hook_ = NULL;
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) {
164 if (!window)
165 return;
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() {
180 delete dialog_;
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;
190 else
191 return EulaHTMLDialog::REJECTED;
194 } // namespace installer