• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
HTMLDialogWin(const std::wstring & url,const std::wstring & param)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 
ShowModal(void * parent_window,CustomizationCallback * callback)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 
GetExtraResult()57   virtual std::wstring GetExtraResult() {
58     return extra_result_;
59   }
60 
61  private:
62   bool InternalDoDialog(CustomizationCallback* callback, int* result);
63   static LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);
64 
65   std::wstring url_;
66   std::wstring param_;
67   static HHOOK hook_;
68   static HINSTANCE mshtml_;
69   static CustomizationCallback* callback_;
70   std::wstring extra_result_;
71 };
72 
CreateNativeHTMLDialog(const std::wstring & url,const std::wstring & param)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.
MsgFilter(int code,WPARAM wParam,LPARAM lParam)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 
InternalDoDialog(CustomizationCallback * callback,int * result)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(&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   } else if (v_result.vt == VT_BSTR) {
144     *result = HTML_DLG_EXTRA;
145     extra_result_.assign(v_result.bstrVal, SysStringLen(v_result.bstrVal));
146   }
147 
148   ::VariantClear(&v_result);
149 
150   if (hook_) {
151     ::UnhookWindowsHookEx(hook_);
152     callback_ = NULL;
153     hook_ = NULL;
154   }
155   return SUCCEEDED(hr);
156 }
157 
158 // EulaHTMLDialog implementation ---------------------------------------------
159 
OnBeforeCreation(wchar_t ** extra)160 void EulaHTMLDialog::Customizer::OnBeforeCreation(wchar_t** extra) {
161 }
162 
163 // The customization of the window consists in removing the close button and
164 // replacing the existing 'e' icon with the standard informational icon.
OnBeforeDisplay(void * window)165 void EulaHTMLDialog::Customizer::OnBeforeDisplay(void* window) {
166   if (!window)
167     return;
168   HWND top_window = static_cast<HWND>(window);
169   LONG_PTR style = ::GetWindowLongPtrW(top_window, GWL_STYLE);
170   ::SetWindowLongPtrW(top_window, GWL_STYLE, style & ~WS_SYSMENU);
171   HICON ico = ::LoadIcon(NULL, IDI_INFORMATION);
172   ::SendMessageW(top_window, WM_SETICON, ICON_SMALL,
173                  reinterpret_cast<LPARAM>(ico));
174 }
175 
EulaHTMLDialog(const std::wstring & file,const std::wstring & param)176 EulaHTMLDialog::EulaHTMLDialog(const std::wstring& file,
177                                const std::wstring& param) {
178   dialog_ = CreateNativeHTMLDialog(file, param);
179 }
180 
~EulaHTMLDialog()181 EulaHTMLDialog::~EulaHTMLDialog() {
182   delete dialog_;
183 }
184 
ShowModal()185 EulaHTMLDialog::Outcome EulaHTMLDialog::ShowModal() {
186   Customizer customizer;
187   HTMLDialog::DialogResult dr = dialog_->ShowModal(NULL, &customizer);
188   if (HTMLDialog::HTML_DLG_ACCEPT == dr)
189     return EulaHTMLDialog::ACCEPTED;
190   else if (HTMLDialog::HTML_DLG_EXTRA == dr)
191     return EulaHTMLDialog::ACCEPTED_OPT_IN;
192   else
193     return EulaHTMLDialog::REJECTED;
194 }
195 
196 }  // namespace installer
197