summaryrefslogtreecommitdiffstats
path: root/chrome/installer/util/html_dialog_impl.cc
blob: 2ba8974c3e633c88488fdc9ce8fc24f7d7635c92 (plain)
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
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>
#include <urlmon.h>

#include "chrome/installer/util/html_dialog.h"

#pragma comment(lib, "urlmon.lib")

namespace {
// Signature of MSHTML.DLL ShowHTMLDlg.
typedef HRESULT (CALLBACK *ShowHTMLDlg)(HWND parent_hwnd,
                                        IMoniker *moniker,
                                        VARIANT *in_args,
                                        TCHAR *options,
                                        VARIANT *out_args);
}  // namespace.


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.
class HTMLDialogWin : public HTMLDialog {
 public:
  HTMLDialogWin(const std::wstring& url) : url_(url) {
    if (!mshtml_)
       mshtml_ = LoadLibrary(L"MSHTML.DLL");
  }

  virtual DialogResult ShowModal(void* parent_window,
                                 CustomizationCallback* callback) {
    if (!InternalDoDialog(callback))
      return HTML_DLG_ERROR;
    // TODO(cpu): Remove the HTML_DLG_ACCEPT and read the real return
    // value from the ShowHTMLDialog call.
    return HTML_DLG_ACCEPT;
  }

  // TODO(cpu): Not yet implemented.
  virtual std::wstring GetExtraResult() {
    return std::wstring();
  }

 private:
  bool InternalDoDialog(CustomizationCallback* callback);
  static LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);

  std::wstring url_;
  static HHOOK hook_;
  static HINSTANCE mshtml_;
  static CustomizationCallback* callback_;
};

HTMLDialog* CreateNativeHTMLDialog(const std::wstring& url) {
  return new HTMLDialogWin(url);
}

HHOOK HTMLDialogWin::hook_ = NULL;
HINSTANCE HTMLDialogWin::mshtml_ = NULL;
HTMLDialogWin::CustomizationCallback* HTMLDialogWin::callback_ = NULL;

// 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) {
  if (!mshtml_)
    return false;
  ShowHTMLDlg show_html_dialog =
      reinterpret_cast<ShowHTMLDlg>(GetProcAddress(mshtml_, "ShowHTMLDialog"));
  if (!show_html_dialog)
    return false;

  IMoniker *url_moniker = NULL;
  ::CreateURLMoniker(NULL, url_.c_str(), &url_moniker);
  if (!url_moniker)  
    return false;

  wchar_t* extra_args = NULL;
  if (callback) {
    callback->OnBeforeCreation(reinterpret_cast<void**>(&extra_args));
    // Sets a windows hook for this thread only.
    hook_ = ::SetWindowsHookEx(WH_GETMESSAGE, MsgFilter, NULL,
                               GetCurrentThreadId());
    if (hook_)
      callback_ = callback;
  }

  // Creates the window with the embedded IE control in a modal loop.
  HRESULT hr = show_html_dialog(NULL, url_moniker, NULL, extra_args, NULL);
  url_moniker->Release();

  if (hook_) {
    ::UnhookWindowsHookEx(hook_);
    callback_ = NULL;
    hook_ = NULL;
  }
  return SUCCEEDED(hr);
}

}  // namespace installer