1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <windows.h>
#include <mshtmhst.h>
#include <urlmon.h>
#include "base/strings/string_util.h"
#include "base/win/scoped_variant.h"
#include "chrome/installer/util/html_dialog.h"
namespace installer {
// Windows implementation of the HTML dialog class. The main danger with
// using the IE embedded control as a child window of a custom window is that
// it still contains too much browser functionality, allowing the user to do
// things that are not expected of a plain dialog. ShowHTMLDialog API solves
// that problem but gives us a not very customizable frame. We solve that
// using hooks to end up with a robust dialog at the expense of having to do
// the buttons in html itself, like so:
//
// <form onsubmit="submit_it(this); return false;">
// <input name="accept" type="checkbox" /> My cool option
// <input name="submit" type="submit" value="[accept]" />
// </form>
//
// function submit_it(f) {
// if (f.accept.checked) {
// window.returnValue = 1; <-- this matches HTML_DLG_ACCEPT
// } else {
// window.returnValue = 2; <-- this matches HTML_DLG_DECLINE
// }
// window.close();
// }
//
// Note that on the submit handler you need to set window.returnValue to one of
// the values of DialogResult and call window.close().
class HTMLDialogWin : public HTMLDialog {
public:
HTMLDialogWin(const std::wstring& url, const std::wstring& param)
: url_(url), param_(param) {
if (!mshtml_)
mshtml_ = LoadLibrary(L"MSHTML.DLL");
}
DialogResult ShowModal(void* parent_window,
CustomizationCallback* callback) override {
int result = HTML_DLG_DECLINE;
if (!InternalDoDialog(callback, &result))
return HTML_DLG_ERROR;
return static_cast<DialogResult>(result);
}
std::wstring GetExtraResult() override { return extra_result_; }
private:
bool InternalDoDialog(CustomizationCallback* callback, int* result);
static LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);
std::wstring url_;
std::wstring param_;
static HHOOK hook_;
static HINSTANCE mshtml_;
static CustomizationCallback* callback_;
std::wstring extra_result_;
};
HTMLDialog* CreateNativeHTMLDialog(const std::wstring& url,
const std::wstring& param) {
return new HTMLDialogWin(url, param);
}
HHOOK HTMLDialogWin::hook_ = nullptr;
HINSTANCE HTMLDialogWin::mshtml_ = nullptr;
HTMLDialogWin::CustomizationCallback* HTMLDialogWin::callback_ = nullptr;
// This hook function gets called for messages bound to the windows that
// ShowHTMLDialog creates. We tell apart the top window because it has the
// system menu style.
LRESULT HTMLDialogWin::MsgFilter(int code, WPARAM wParam, LPARAM lParam) {
static bool tweak_window = true;
if (lParam && tweak_window) {
HWND target_window = reinterpret_cast<MSG*>(lParam)->hwnd;
if (target_window) {
LONG_PTR style = ::GetWindowLongPtrW(target_window, GWL_STYLE);
if (style & WS_SYSMENU) {
tweak_window = false;
callback_->OnBeforeDisplay(target_window);
}
}
}
// Always call the next hook in the chain.
return ::CallNextHookEx(hook_, code, wParam, lParam);
}
bool HTMLDialogWin::InternalDoDialog(CustomizationCallback* callback,
int* result) {
if (!mshtml_)
return false;
SHOWHTMLDIALOGFN* show_html_dialog = reinterpret_cast<SHOWHTMLDIALOGFN*>(
GetProcAddress(mshtml_, "ShowHTMLDialog"));
if (!show_html_dialog)
return false;
IMoniker* url_moniker = nullptr;
::CreateURLMonikerEx(nullptr, url_.c_str(), &url_moniker, URL_MK_UNIFORM);
if (!url_moniker)
return false;
wchar_t* extra_args = nullptr;
if (callback) {
callback->OnBeforeCreation(&extra_args);
// Sets a windows hook for this thread only.
hook_ = ::SetWindowsHookEx(WH_GETMESSAGE, MsgFilter, nullptr,
GetCurrentThreadId());
if (hook_)
callback_ = callback;
}
// Pass our parameter to the dialog in the dialogArguments property of
// the window object.
base::win::ScopedVariant dialog_args(param_.c_str());
VARIANT v_result;
::VariantInit(&v_result);
// Creates the window with the embedded IE control in a modal loop.
HRESULT hr = show_html_dialog(nullptr, url_moniker, dialog_args.AsInput(),
extra_args, &v_result);
url_moniker->Release();
if (v_result.vt == VT_I4) {
*result = v_result.intVal;
} else if (v_result.vt == VT_BSTR) {
*result = HTML_DLG_EXTRA;
extra_result_.assign(v_result.bstrVal, SysStringLen(v_result.bstrVal));
}
::VariantClear(&v_result);
if (hook_) {
::UnhookWindowsHookEx(hook_);
callback_ = nullptr;
hook_ = nullptr;
}
return SUCCEEDED(hr);
}
// EulaHTMLDialog implementation ---------------------------------------------
void EulaHTMLDialog::Customizer::OnBeforeCreation(wchar_t** extra) {}
// The customization of the window consists in removing the close button and
// replacing the existing 'e' icon with the standard informational icon.
void EulaHTMLDialog::Customizer::OnBeforeDisplay(void* window) {
if (!window)
return;
HWND top_window = static_cast<HWND>(window);
LONG_PTR style = ::GetWindowLongPtrW(top_window, GWL_STYLE);
::SetWindowLongPtrW(top_window, GWL_STYLE, style & ~WS_SYSMENU);
HICON ico = ::LoadIcon(nullptr, IDI_INFORMATION);
::SendMessageW(top_window, WM_SETICON, ICON_SMALL,
reinterpret_cast<LPARAM>(ico));
}
EulaHTMLDialog::EulaHTMLDialog(const std::wstring& file,
const std::wstring& param) {
dialog_ = CreateNativeHTMLDialog(file, param);
}
EulaHTMLDialog::~EulaHTMLDialog() {
delete dialog_;
}
EulaHTMLDialog::Outcome EulaHTMLDialog::ShowModal() {
Customizer customizer;
HTMLDialog::DialogResult dr = dialog_->ShowModal(nullptr, &customizer);
if (HTMLDialog::HTML_DLG_ACCEPT == dr)
return EulaHTMLDialog::ACCEPTED;
else if (HTMLDialog::HTML_DLG_EXTRA == dr)
return EulaHTMLDialog::ACCEPTED_OPT_IN;
else
return EulaHTMLDialog::REJECTED;
}
} // namespace installer
|