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. 4 5 #include <windows.h> 6 #include <mshtmhst.h> 7 #include <urlmon.h> 8 9 #include "base/win/scoped_variant.h" 10 #include "chrome/installer/util/html_dialog.h" 11 12 #pragma comment(lib, "urlmon.lib") 13 14 namespace installer { 15 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: 23 // 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> 28 // 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 // } 37 // 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(). 40 41 class HTMLDialogWin : public HTMLDialog { 42 public: 43 HTMLDialogWin(const std::wstring& url, const std::wstring& param) 44 : url_(url), param_(param) { 45 if (!mshtml_) 46 mshtml_ = LoadLibrary(L"MSHTML.DLL"); 47 } 48 49 virtual DialogResult ShowModal(void* parent_window, 50 CustomizationCallback* callback) { 51 int result = HTML_DLG_DECLINE; 52 if (!InternalDoDialog(callback, &result)) 53 return HTML_DLG_ERROR; 54 return static_cast<DialogResult>(result); 55 } 56 57 // TODO(cpu): Not yet implemented. 58 virtual std::wstring GetExtraResult() { 59 return std::wstring(); 60 } 61 62 private: 63 bool InternalDoDialog(CustomizationCallback* callback, int* result); 64 static LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam); 65 66 std::wstring url_; 67 std::wstring param_; 68 static HHOOK hook_; 69 static HINSTANCE mshtml_; 70 static CustomizationCallback* callback_; 71 }; 72 73 HTMLDialog* CreateNativeHTMLDialog(const std::wstring& url, 74 const std::wstring& param) { 75 return new HTMLDialogWin(url, param); 76 } 77 78 HHOOK HTMLDialogWin::hook_ = NULL; 79 HINSTANCE HTMLDialogWin::mshtml_ = NULL; 80 HTMLDialogWin::CustomizationCallback* HTMLDialogWin::callback_ = NULL; 81 82 // This hook function gets called for messages bound to the windows that 83 // ShowHTMLDialog creates. We tell apart the top window because it has the 84 // system menu style. 85 LRESULT HTMLDialogWin::MsgFilter(int code, WPARAM wParam, LPARAM lParam) { 86 static bool tweak_window = true; 87 if (lParam && tweak_window) { 88 HWND target_window = reinterpret_cast<MSG*>(lParam)->hwnd; 89 if (target_window) { 90 LONG_PTR style = ::GetWindowLongPtrW(target_window, GWL_STYLE); 91 if (style & WS_SYSMENU) { 92 tweak_window = false; 93 callback_->OnBeforeDisplay(target_window); 94 } 95 } 96 } 97 // Always call the next hook in the chain. 98 return ::CallNextHookEx(hook_, code, wParam, lParam); 99 } 100 101 bool HTMLDialogWin::InternalDoDialog(CustomizationCallback* callback, 102 int* result) { 103 if (!mshtml_) 104 return false; 105 SHOWHTMLDIALOGFN* show_html_dialog = 106 reinterpret_cast<SHOWHTMLDIALOGFN*>( 107 GetProcAddress(mshtml_, "ShowHTMLDialog")); 108 if (!show_html_dialog) 109 return false; 110 111 IMoniker *url_moniker = NULL; 112 ::CreateURLMonikerEx(NULL, url_.c_str(), &url_moniker, URL_MK_UNIFORM); 113 if (!url_moniker) 114 return false; 115 116 wchar_t* extra_args = NULL; 117 if (callback) { 118 callback->OnBeforeCreation(reinterpret_cast<void**>(&extra_args)); 119 // Sets a windows hook for this thread only. 120 hook_ = ::SetWindowsHookEx(WH_GETMESSAGE, MsgFilter, NULL, 121 GetCurrentThreadId()); 122 if (hook_) 123 callback_ = callback; 124 } 125 126 // Pass our parameter to the dialog in the dialogArguments property of 127 // the window object. 128 base::win::ScopedVariant dialog_args(param_.c_str()); 129 130 VARIANT v_result; 131 ::VariantInit(&v_result); 132 133 // Creates the window with the embedded IE control in a modal loop. 134 HRESULT hr = show_html_dialog(NULL, 135 url_moniker, 136 dialog_args.AsInput(), 137 extra_args, 138 &v_result); 139 url_moniker->Release(); 140 141 if (v_result.vt == VT_I4) 142 *result = v_result.intVal; 143 ::VariantClear(&v_result); 144 145 if (hook_) { 146 ::UnhookWindowsHookEx(hook_); 147 callback_ = NULL; 148 hook_ = NULL; 149 } 150 return SUCCEEDED(hr); 151 } 152 153 // EulaHTMLDialog implementation --------------------------------------------- 154 155 void EulaHTMLDialog::Customizer::OnBeforeCreation(void** extra) { 156 } 157 158 // The customization of the window consists in removing the close button and 159 // replacing the existing 'e' icon with the standard informational icon. 160 void EulaHTMLDialog::Customizer::OnBeforeDisplay(void* window) { 161 if (!window) 162 return; 163 HWND top_window = static_cast<HWND>(window); 164 LONG_PTR style = ::GetWindowLongPtrW(top_window, GWL_STYLE); 165 ::SetWindowLongPtrW(top_window, GWL_STYLE, style & ~WS_SYSMENU); 166 HICON ico = ::LoadIcon(NULL, IDI_INFORMATION); 167 ::SendMessageW(top_window, WM_SETICON, ICON_SMALL, 168 reinterpret_cast<LPARAM>(ico)); 169 } 170 171 EulaHTMLDialog::EulaHTMLDialog(const std::wstring& file, 172 const std::wstring& param) { 173 dialog_ = CreateNativeHTMLDialog(file, param); 174 } 175 176 EulaHTMLDialog::~EulaHTMLDialog() { 177 delete dialog_; 178 } 179 180 EulaHTMLDialog::Outcome EulaHTMLDialog::ShowModal() { 181 Customizer customizer; 182 HTMLDialog::DialogResult dr = dialog_->ShowModal(NULL, &customizer); 183 if (HTMLDialog::HTML_DLG_ACCEPT == dr) 184 return EulaHTMLDialog::ACCEPTED; 185 else if (HTMLDialog::HTML_DLG_EXTRA == dr) 186 return EulaHTMLDialog::ACCEPTED_OPT_IN; 187 else 188 return EulaHTMLDialog::REJECTED; 189 } 190 191 } // namespace installer 192